---
title: 'Admin, installation, and OAuth performance'
description: >-
  Learn how to improve the performance of apps embedded in the Shopify admin,
  and for your OAuth flow.
source_url:
  html: 'https://shopify.dev/docs/apps/build/performance/admin-installation-oauth'
  md: 'https://shopify.dev/docs/apps/build/performance/admin-installation-oauth.md'
---

# Admin, installation, and OAuth performance

These guidelines apply to [apps rendered in the Shopify admin](https://shopify.dev/docs/apps/build/app-home) only.

***

## Improve your app's loading performance

Loading performance is an important part of the user experience when using apps rendered in the Shopify admin. When your app is slow to load, it dissuades users from adopting it, increases bounce rates, and decreases overall usage.

You can also improve your app's loading experience by minimizing visual noise, such as layout shifts, and clearly indicating loading progress. As a result, users perceive your app as faster and more responsive, even if the loading time doesn't change.

### Measure your app's loading performance

Shopify uses Web Vitals to measure your app's performance in the Shopify admin. Each time a user loads your app, we gather and save the Web Vitals metrics in order to better understand the user experience. To learn more about Web Vitals, you can refer to the introduction on [web.dev](https://web.dev/vitals/).

![Diagram showing sequence of Web Vitals metrics on an app rendered in the Shopify admin.](https://shopify.dev/assets/assets/images/apps/best-practices/performance/webvitalsdiagram-CzMYjLzp.png)

**Note:**

To enable Shopify to gather Web Vitals metrics, you might need to update to the latest version of App Bridge by adding the [App Bridge script](https://shopify.dev/docs/api/app-home#getting-started) to your app's `head`. The App Bridge script can exist alongside App Bridge installed as a packaged dependency and does not require a full migration.

Updating your app to a version that supports admin performance tracking is a prerequisite to reaching [Built for Shopify](https://shopify.dev/docs/apps/store/built-for-shopify) status.

Shopify's Web Vitals package measures your app's performance each time a merchant launches your app through any route. Although Shopify doesn't modify scores provided by Web Vitals, there are some differences in how they're recorded in apps rendered in the Shopify admin compared to standard web development:

* Apps rendered in the Shopify admin run in iFrames, so tools like [Lighthouse](https://shopify.dev/docs/storefronts/themes/tools/lighthouse-ci) might not accurately capture performance.

* To normalize collecting metrics across different application stacks, Shopify records Web Vitals events in a separate runtime from your application.

  To help you use performance data and ensure your app is performing optimally, Shopify provides the following key features:

* The ability to monitor Web Vitals in real-time

* Tools for debugging

  The following sections describe how you can use these features to enhance your app's performance.

#### Monitor Web Vitals

The Web Vitals API allows you to access performance metrics directly in your app, giving you real-time insights into your app's performance. You can implement it using the following code:

```html
<head>
  <script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
  <script>
    /**
     * Callback function for processing Web Vitals metrics.
     * @param {Object} metrics - The metrics object containing Web Vitals data.
     * @param {string} metrics.appId - Identifier for the app (e.g., "gid://shopify/App/1").
     * @param {string} metrics.shopId - Identifier for the shop (e.g., "10").
     * @param {string} metrics.userId - Identifier for the user (e.g., "5").
     * @param {string} metrics.appLoadId - Unique identifier for the current app load session.
     * @param {Array<Object>} metrics.metrics - Array of Web Vitals metrics.
     * @param {string} metrics.metrics[].id - Unique identifier for the metric.
     * @param {string} metrics.metrics[].name - Name of the metric (e.g., "LCP", "FCP", "CLS").
     * @param {number} metrics.metrics[].value - Value of the metric (can be integer or float).
  */
  function processWebVitals(metrics) {
      const monitorUrl = 'https://yourserver.com/web-vitals-metrics';
      const data = JSON.stringify(metrics);


      navigator.sendBeacon(monitorUrl, data);
    }


    // Register the callback
    shopify.webVitals.onReport(processWebVitals);
  </script>
</head>
```

The callback is called with Web Vitals data, allowing you to send it to your own server for analysis. Your app also receives country-level geo information for each web vitals event:

* This field lets you understand performance based on location, enabling region-specific debugging and optimizations.
* No additional setup required to receive country-level information. Existing callbacks automatically receive the country field.
* Apps can segment performance by geography for more accurate diagnostics.

By monitoring these metrics, you can identify performance issues as they occur and make necessary optimizations.

You can also see the values Shopify records in the [Dev Dashboard](https://shopify.dev/docs/apps/build/dev-dashboard/monitoring-and-logs).

#### Debugging Web Vitals

While monitoring provides ongoing insights, sometimes you need to dig deeper to understand specific performance issues. To help with this, Shopify provides a debugging tool for Web Vitals.

To enable detailed logging of Web Vitals as they're recorded and saved, add the `shopify-debug` meta flag to your application's head:

```html
<head>
    <!-- to enable Web Vitals debug logging, add the shopify-debug meta tag -->
    <meta name="shopify-debug" content="web-vitals" />
    <script src="https://cdn.shopify.com/shopifycloud/app-bridge.js" />
</head>
```

The debug flag enables two sets of console logs that provide valuable information about your app's performance:

* Real-time logs directly from Web Vitals, including attribution data for slow-loading elements or inefficient routines

![Realtime logging piped straight from Web Vitals](https://shopify.dev/assets/assets/images/apps/best-practices/performance/real-time-web-vitals-logging-D9AstuUc.png)

* Send-time logs showing the final values recorded by Shopify

![send-time logging to see final values recorded](https://shopify.dev/assets/assets/images/apps/best-practices/performance/report-time-web-vitals-logging-tzGONwSP.png)

**Note:**

If you use another tool to measure your page's performance, it might take measurements more frequently. For example, some tools might measure the performance of each navigation between pages of your app. This can cause discrepancies between the values Shopify records and the metrics gathered by these tools.

***

## Built for Shopify performance criteria

Apps that meet the mandatory criteria on this page, and meet other applicable criteria, are eligible for [Built for Shopify](https://shopify.dev/docs/apps/launch/built-for-shopify) status in the Shopify App Store.

### Largest Contentful Paint

[Largest Contentful Paint (LCP)](https://web.dev/lcp/) measures the time from when a user begins loading your page to when the largest image or block of text is displayed. It tracks how quickly your app can display its main content to users.

**Mandatory:**

75% of the time, your app should have a Largest Contentful Paint of 2.5 seconds or less, measured over a 28 day period.

### Cumulative Layout Shift

[Cumulative Layout Shift (CLS)](https://web.dev/cls/) measures your app's visual stability. Unstable user interfaces can be frustrating for users, especially when an element, such as a button, that they're trying to interact with moves suddenly. Cumulative Layout Shift quantifies these kinds of disruptive experiences so you can identify and remove them.

**Mandatory:**

75% of the time, your app should have a Cumulative Layout Shift of 0.1 or less, measured over a 28 day period.

### Interaction to Next Paint

[Interaction to Next Paint](https://web.dev/inp/) measures a page's overall responsiveness to user interactions by observing the latency of all click, tap and keyboard interactions that occur throughout the lifespan of a user's visit to a page. A low INP score means your app is able to quickly respond to all, or the vast majority, of user interactions. Interaction to Next Paint is only collected for apps using the latest version of App Bridge.

**Mandatory:**

75% of the time, your app should have an Interaction to Next Paint of 200 milliseconds or less, measured over a 28 day period.

***

## Optimize your OAuth flow

Because app authentication is the first interaction that users have with your app UI, you should make sure that it's a positive experience.

Shopify strongly recommends you build your app with Shopify CLI and the React Router app template, which already has OAuth implemented. The template uses [token exchange](https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens#token-exchange-grant), which trades a short-lived ID token for an access token instead of redirecting the merchant, so authorization stays fast. Using the template also helps to ensure that your implementation is complete and follows our OAuth best practices. [Learn more](https://shopify.dev/docs/apps/build/authentication-authorization/cli-app-authentication).

If you don't want to use an app template, then you can [set up token exchange yourself](https://shopify.dev/docs/apps/build/authentication-authorization/implement-token-exchange).

If your app still redirects merchants through `/admin/oauth/authorize`, see [Migrate from legacy OAuth](https://shopify.dev/docs/apps/build/authentication-authorization/cli-app-authentication#migrate-from-legacy-oauth).

Refer to [About app authentication](https://shopify.dev/docs/apps/build/authentication-authorization) for any other kind of app, including apps built on your own tech stack.

***
