# Shopify Developer Platform
The Shopify developer platform enables developers to create and customize applications, themes, and integrations for Shopify's e-commerce ecosystem. It provides robust APIs, development tools, and extensive documentation to facilitate the building of tailored solutions for merchants.
Here are some examples of what developers can build with Shopify:
* Build apps that extend the functionality of Shopify stores. For example, apps for inventory management, analytics and reporting, or customer loyalty programs.
* Extend Shopify’s checkout with extensions that change its UI or business logic.
* Customize a store’s theme to match the merchant’s brand or change the layout.
* Build online storefronts that allow buyers to shop in a merchant’s store.
* Provide custom admin workflows for merchants that are not included in the Shopify core product. For example, subscription management, email marketing, and live customer support.
* Integrate external systems like ERPs, CRMs, accounting systems into Shopify to import/export data.
## Apps
Shopify Apps are third-party applications that can be integrated into Shopify stores to enhance functionality and improve the e-commerce experience. These apps cover a wide range of features, including inventory management, marketing tools, customer support, shipping solutions, and analytics, allowing merchants to customize their stores according to their specific needs.
Developers can create their own apps using Shopify's APIs and resources, and they can publish them on the Shopify App Store for merchants to discover and install. This ecosystem supports a vibrant community of developers and provides merchants with numerous options to optimize their online businesses.
Shopify apps can appear in and add functionality to nearly every area of the Shopify platform. A single app can add functionality to multiple areas of the platform.
### Configuration and shopify.app.toml
When you initialize an app using the CLI (shopify app init CLI command) you will receive a folder structure that contains a `shopify.app.toml` file at the root. This file helps you describe what capabilities you want your app to have and Shopify ensures that this app configuration is then instantiated on every shop where the app is installed.
You can use scopes to configure the scopes that your app requires, and webhook subscriptions to detail out what events your app should receive.
```
name = "Example App"
client_id = "a61950a2cbd5f32876b0b55587ec7a27"
application_url = "https://www.app.example.com/"
embedded = true
handle = "example-app"
[access_scopes]
scopes = "read_products, write_products"
[access.admin]
direct_api_mode = "online"
[auth]
redirect_urls = [
"https://app.example.com/api/auth/callback",
"https://app.example.com/api/auth/oauth/callback",
]
[webhooks]
api_version = "2024-01"
[[webhooks.subscriptions]]
topics = [ "app/uninstalled" ]
compliance_topics = [ "customers/redact", "customers/data_request", "shop/redact" ]
uri = "/webhooks"
[app_proxy]
url = "https://app.example.com/api/proxy"
subpath = "store-pickup"
prefix = "apps"
[pos]
embedded = false
[app_preferences]
url = "https://www.app.example.com/preferences"
[build]
automatically_update_urls_on_dev = false
```
If you are working with multiple environments (dev, staging, production) it is recommended that you have a different `shopify.app.toml` file for each environment. The Shopify CLI makes this easy by allowing you to link multiple Shopify apps to your codebase, so that you can dedicate specific apps and their configuration for various development, staging, and production workflows.
You would create a new configuration using the command `shopify app config link` and then you can tell the CLI to use a specific configuration with the use command and so if you had a TOML configuration called `development` you would say `shopify app config use development` in the CLI.
For more, see [App configuration](https://shopify.dev/docs/apps/build/cli-for-apps/app-configuration) and [Manage app config files](https://shopify.dev/docs/apps/build/cli-for-apps/manage-app-config-files).
### Admin API
The [GraphQL Admin API](https://shopify.dev/docs/api/admin-graphql) lets you build apps and integrations that extend and enhance the Shopify admin. You can query and manage resources like products, customers, orders, inventory, fulfillment, and more. Because it’s GraphQL, it provides a more flexible, efficient, and controlled way to query and manipulate data than traditional REST APIs. For more on how to work with GraphQL, see [About GraphQL](https://shopify.dev/docs/apps/build/graphql).
The Admin GraphQL endpoint URL depends on your shop name and the API version:
```
https://{shop-name}.myshopify.com/admin/api/2025-01/graphql.json
```
#### Authentication
Authentication requires both access tokens and specific access scopes depending what resource you need to access.
- All GraphQL Admin API queries require a valid Shopify access token.
- Public and custom apps created in the Partner Dashboard generate tokens using OAuth, and custom apps made in the Shopify admin are authenticated in the Shopify admin.
- Include your token as a `X-Shopify-Access-Token` header on all API queries.
- Your app will need the correct [access scopes](https://shopify.dev/api/usage/access-scopes).
#### Example queries
Get shop information:
```
query { shop { id name email domain createdAt updatedAt } }
```
Get products:
```
query { products(first: 10) { edges { node { id title description variants(first: 5) { edges { node { id price sku } } } } } } }
```
Get orders:
```
query { orders(first: 10) { edges { node { id name totalPrice createdAt lineItems(first: 5) { edges { node { title quantity } } } } } } }
```
Get customers:
```
query { customers(first: 10) { edges { node { id firstName lastName email ordersCount } } } }
```
#### Example mutations
Create a product:
```
mutation { productCreate(input: { title: "New Product" bodyHtml: "Good Product" vendor: "Vendor Name" productType: "Type" tags: "tag1, tag2" variants: [{ price: "19.99" sku: "SKU123" }] }) { product { id title } userErrors { field message } } }
```
Update a product:
```
mutation { productUpdate(input: { id: "gid://shopify/Product/1234567890" title: "Updated Product Title" }) { product { id title } userErrors { field message } } }
```
Delete a product:
```
mutation { productDelete(id: "gid://shopify/Product/1234567890") { deletedProductId userErrors { field message } } }
```
#### Best practices
* **Paginate results**: Use first, last, after, and before for pagination in queries.
* **Error handling**: Always check for userErrors in mutations to handle any issues that may arise.
* **Rate limiting**: Be aware of rate limits (typically 2 requests per second) and implement retries or exponential backoff if necessary.
* **Use fragments**: For commonly used fields across multiple queries, consider defining fragments to keep your queries DRY.
### Shopify CLI
The [Shopify CLI](https://shopify.dev/docs/api/shopify-cli) is a command-line interface tool that helps you generate and work with Shopify apps, themes and custom storefronts. You can also use it to automate many common development tasks. Using the CLI makes it faster and easier to build on Shopify.
The general syntax for CLI commands is:
```
shopify [topic] [command]
```
Here are some examples of common commands:
* Install the Shopify CLI: `npm install -g @shopify/cli@latest`
* Create a new app: `shopify app init`
* Serve your app locally: `shopify app dev`
* Deploy your app: `shopify app deploy`
* Retrieve your theme files from Shopify: `shopify theme pull`
* Upload your theme to preview it: `shopify theme dev`
* Generate an extension: `shopify app generate extension`
You can find all the commands in the Shopify dev docs:
* [App commands](https://shopify.dev/docs/api/shopify-cli/app)
* [Theme commands](https://shopify.dev/docs/api/shopify-cli/theme)
* [Hydrogen commands](https://shopify.dev/docs/api/shopify-cli/hydrogen)
* [General commands](https://shopify.dev/docs/api/shopify-cli/general-commands)
You can also use `shopify help` to get help within the CLI.
### Polaris Web Components
Polaris Web Components are Shopify's unified UI toolkit built on web platform standards. They provide consistent design and functionality across all Shopify surfaces including Admin, Checkout, Customer accounts, and POS. Built with actual web components technology, they're framework-agnostic and work with any JavaScript library or framework.
#### Why use Polaris Web Components?
We base our design guidelines on some basic principles, so you can build apps that are predictable and easy to use. Here are four key reasons to use Polaris Web Components:
* **Built for Shopify**: Apps must meet all directives to qualify for the [Built for Shopify](https://shopify.dev/docs/apps/launch/built-for-shopify) program.
* **A better merchant experience**: Merchants expect a predictable user experience that works like the rest of the Shopify admin.
* **Framework agnostic**: Works with React, Vue, vanilla JS, or any framework using standard HTML.
* **Accessible**: Built-in accessibility features ensure great experiences for all users.
* **Consistent**: Use the same components across all Shopify surfaces.
#### Setup Polaris Web Components
**Required script tag:**
```html
```
**For TypeScript projects:**
```bash
npm install -D @shopify/polaris-types@latest
```
**Configure TypeScript:**
```json
{
"compilerOptions": {
"types": ["@shopify/polaris-types"]
}
}
```
**For React projects:**
See our [App template](https://github.com/Shopify/shopify-app-template-react-router).
Getting set up with [React router](https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-app-react-router/src/react/components/AppProvider/AppProvider.tsx#L111-L126)
#### Best practices
* **Follow accessibility guidelines**: [Ensure your app is accessible](https://shopify.dev/docs/apps/build/accessibility); Polaris components are designed with accessibility in mind.
* **Keep design consistent**: Stick to the guidelines provided in the Polaris documentation to maintain a cohesive user experience.
* **Use the latest version**: The CDN script automatically provides the latest version of components.
#### Common Polaris Web Components
You can find all the Polaris web components in the [Polaris documentation](/docs/api/app-home/polaris-web-components). Here are a few of the most common components along with their syntax.
##### Box
Box components provide layout and spacing control for content grouping. They're used similarly to cards for organizing content.
```html
Content inside a boxAdditional text content goes here
```
##### Page Layout
Pages are structured using s-page as the main container with sections for content organization.
```html
BackDuplicateViewSaveCredit card information
```
##### Grid Layout
Grid components are used for creating responsive layouts with consistent spacing and alignment.
```html
Online store dashboardView a summary of your online store's performance.AnalyticsTrack your store performance.
```
##### Button
Buttons are used in Polaris primarily for actions, such as "Add", "Close", "Cancel", or "Save".
```html
Add productCancelDeleteAdd productProcessing...View products
```
##### Text Field
Text fields are used as input fields that merchants can type into. They support various formats including text, email, and numbers.
```html
```
##### Checkbox
Checkboxes are most commonly used in Polaris to give merchants a way to make a range of selections (zero, one, or multiple).
```html
```
##### Choice List
Choice lists are used to present options where merchants must make a single selection. The choice list automatically handles radio button behavior when multiple=false and checkbox behavior when multiple=true.
```html
HiddenOptionalRequired
```
##### Additional Common Components
**Badge**: Used to highlight status or provide quick visual context.
```html
ActivePendingErrorFeatured
```
**Banner**: Used for important messaging that affects the entire page or section.
```html
This order was archived on March 7, 2017 at 3:12pm EDT.
```
**Modal**: Used for focused tasks that require user attention.
```html
Are you sure you want to delete this product? This action cannot be undone.DeleteCancel
```
**Stack**: Used for flexible layout with consistent spacing.
```html
Customer informationEmail: customer@example.comPhone: +1 (555) 123-4567SaveCancel
```
### Polaris
Shopify Polaris is the design system used by Shopify to create a consistent user interface across applications. We believe that the best apps provide merchants with a user experience that matches the appearance and behaviors of the Shopify admin UI. Using Polaris lets you achieve that consistency.
#### Why follow Polaris?
We base our design guidelines on some basic principles, so you can build apps that are predictable and easy to use. Here are four key reasons to follow the guidelines:
* **Built for Shopify**: Apps must meet all directives to qualify for the [Built for Shopify](https://shopify.dev/docs/apps/launch/built-for-shopify) program.
* **A better merchant experience**: Merchants expect a predictable user experience that works like the rest of the Shopify admin.
* **Adaptive:** Designing for mobile devices must be at the forefront of the app building process.
* **Accessible**: To provide a great experience for all Shopify merchants and their customers, apps must be built using accessibility best practices.
#### Install Polaris
```
npm install @shopify/polaris
```
#### Best practices
* **Use components**: Always use Polaris components for consistency and accessibility.
* **Follow accessibility guidelines**: [Ensure your app is accessible](https://shopify.dev/docs/apps/build/accessibility); Polaris components are designed with accessibility in mind.
* **Keep design consistent**: Stick to the guidelines provided in the Polaris documentation to maintain a cohesive user experience.
* **Use the latest version**: Regularly check for updates to Polaris to take advantage of new components and features.
#### Common Polaris Components
You can find all the Polaris components on [polaris.shopify.com](https://polaris.shopify.com/). Here are a few of the most common components along with their syntax.
##### Card
Cards are used in Polaris to group similar concepts and tasks together for merchants to scan, read, and get things done. It displays content in a familiar and recognizable style.
```
import {Card, Text} from '@shopify/polaris';
import React from 'react';
function CardDefault() {
return (
Content inside a card
);
}
```
##### Page
Pages are used in Polaris to build the outer wrapper of a page, including the page title and associated actions.
```
import {Page, Badge, LegacyCard} from '@shopify/polaris';
import React from 'react';
function PageExample() {
return (
Paid}
subtitle="Perfect for any pet"
compactTitle
primaryAction={{content: 'Save', disabled: true}}
secondaryActions={[
{
content: 'Duplicate',
accessibilityLabel: 'Secondary action label',
onAction: () => alert('Duplicate action'),
},
{
content: 'View on your store',
onAction: () => alert('View on your store action'),
},
]}
actionGroups={[
{
title: 'Promote',
actions: [
{
content: 'Share on Facebook',
accessibilityLabel: 'Individual action label',
onAction: () => alert('Share on Facebook action'),
},
],
},
]}
pagination={{
hasPrevious: true,
hasNext: true,
}}
>
Credit card information
);
}
```
##### Layout
The layout component is used in Polaris to create the main layout on a page. Layouts sections come in three main configurations. one-column, two-column, and annotated.
```
import {Page, Layout, LegacyCard} from '@shopify/polaris';
import React from 'react';
function LayoutExample() {
return (
View a summary of your online store’s performance.
);
}
```
##### Tabs
Tabs are used in Polaris to alternate among related views within the same context.
```
import {LegacyCard, Tabs} from '@shopify/polaris';
import {useState, useCallback} from 'react';
function TabsDefaultExample() {
const [selected, setSelected] = useState(0);
const handleTabChange = useCallback(
(selectedTabIndex: number) => setSelected(selectedTabIndex),
[],
);
const tabs = [
{
id: 'all-customers-1',
content: 'All',
accessibilityLabel: 'All customers',
panelID: 'all-customers-content-1',
},
{
id: 'accepts-marketing-1',
content: 'Accepts marketing',
panelID: 'accepts-marketing-content-1',
},
{
id: 'repeat-customers-1',
content: 'Repeat customers',
panelID: 'repeat-customers-content-1',
},
{
id: 'prospects-1',
content: 'Prospects',
panelID: 'prospects-content-1',
},
];
return (
Tab {selected} selected
);
}
```
##### Button
Buttons are used in Polaris primarily for actions, such as “Add”, “Close”, “Cancel”, or “Save”.
```
import {Button} from '@shopify/polaris';
import React from 'react';
function ButtonExample() {
return ;
}
```
##### TextField
A text field are used in Polaris as input fields that merchants can type into. It has a range of options and supports several text formats including numbers.
```
import {TextField} from '@shopify/polaris';
import {useState, useCallback} from 'react';
function TextFieldExample() {
const [value, setValue] = useState('Jaded Pixel');
const handleChange = useCallback(
(newValue: string) => setValue(newValue),
[],
);
return (
);
}
```
##### Checkbox
Checkboxes are most commonly used in Polaris to give merchants a way to make a range of selections (zero, one, or multiple).
```
import {Checkbox} from '@shopify/polaris';
import {useState, useCallback} from 'react';
function CheckboxExample() {
const [checked, setChecked] = useState(false);
const handleChange = useCallback(
(newChecked: boolean) => setChecked(newChecked),
[],
);
return (
);
}
```
##### Radio button
Radio buttons are used in Polaris to present each item in a list of options where merchants must make a single selection.
```
import {LegacyStack, RadioButton} from '@shopify/polaris';
import {useState, useCallback} from 'react';
function RadioButtonExample() {
const [value, setValue] = useState('disabled');
const handleChange = useCallback(
(_: boolean, newValue: string) => setValue(newValue),
[],
);
return (
);
}
```
### Embedded apps and App bridge
The primary place where users engage with your app is its app home. This is the location where merchants are directed when they navigate to your app in Shopify.
The Shopify admin provides a surface for apps to render the UX for their app home. On the web, the surface is an iframe and in the Shopify mobile app, the surface is a WebView.
By combining Shopify App Bridge and Polaris, you can make your app display seamlessly in the Shopify admin. Polaris enables apps to match the visual appearance of the admin by using the same design components. App Bridge enables apps to communicate with the Shopify admin and create UI elements outside of the app's surface. Such elements include navigation menus, modals that cover the entire screen, and contextual save bars that prevent users from navigating away from the page when they have unsaved changes.
#### App Bridge
The App Bridge library provides APIs that enable Shopify apps to render UI in the Shopify app home surface.
Apps built with Shopify App Bridge are more performant, flexible, and seamlessly integrate with the Shopify admin. You can use Shopify App Bridge with Polaris to provide a consistent and intuitive user experience that matches the rest of the Shopify admin.
On the web, your app renders in an iframe and in the Shopify mobile app it renders in a WebView.
The latest version of App Bridge is built on top of web components and APIs to provide a flexible and familiar development environment. Your app can invoke these APIs using vanilla JavaScript functions.
App Bridge enables you to do the following from your app home:
* Render a navigation menu on the left of the Shopify admin
```
HomeTemplatesSettings
```
* Render a contextual save bar above the top bar of the Shopify admin
```
```
* Render a title bar with primary and secondary actions
```
```
### Checkout UI extensions
[Checkout UI Extensions](https://shopify.dev/docs/api/checkout-ui-extensions) allow developers to customize the checkout experience for Shopify stores. They allow merchants to add custom fields, promotional messages, and more.
#### Key concepts
* [**Extension targets**](https://shopify.dev/docs/api/checkout-ui-extensions/2025-01/extension-targets-overview): Extension targets provide locations where merchants can insert custom content.
* **Static extension targets** are tied to core checkout features like contact information, shipping methods, and order summary line items.
* **Block extension targets** can be displayed at any point in the checkout process and will always render regardless of which checkout features are available.
* **Configuration file**: The `shopify.extension.toml` contains the extension's configuration, which includes the extension name, extension targets, metafields, capabilities, and settings definition.
* [**Extension APIs**](https://shopify.dev/docs/api/checkout-ui-extensions/2025-01/apis): APIs enable checkout UI extensions to get information about the checkout or related objects and to perform actions
* [**UI components**](https://shopify.dev/docs/api/checkout-ui-extensions/2025-01/components): Checkout UI extensions declare their interface using supported UI components. Shopify renders the UI natively, so it's performant, accessible, and works in all of checkout's supported browsers.
* **Security**: Checkout UI extensions are a safe and secure way to customize the appearance and functionality of the checkout page without compromising the security of checkout or customer data.
#### Create a new extension
To create a new extension, use the Shopify CLI:
```
shopify app generate extension
```
#### Common components
##### View
View in checkout UI extensions is a generic container component. Its contents will always be their “natural” size, so this component can be useful in layout components.
```
import {
reactExtension,
View,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => ,
);
function Extension() {
return (
View
);
}
```
##### InlineLayout
InlineLayout in checkout UI extensions is used to lay out content over multiple columns.
```
import {
reactExtension,
InlineLayout,
View,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => ,
);
function Extension() {
return (
20%
fill
);
}
```
##### Button
Buttons in checkout UI extensions are used for actions, such as “Add”, “Continue”, “Pay now”, or “Save”.
```
import {
reactExtension,
Button,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => ,
);
function Extension() {
return (
);
}
```
##### Link
Links in checkout UI extensions make text interactive so customers can perform an action, such as navigating to another location.
```
import {
reactExtension,
Link,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => ,
);
function Extension() {
return (
Sustainability fund
);
}
```
##### Modal
Modals in checkout UI extensions are a special type of overlay that shift focus towards a specific action/set of information before the main flow can proceed.
```
import {
reactExtension,
useApi,
Button,
Link,
Modal,
TextBlock,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => ,
);
function Extension() {
const {ui} = useApi();
return (
We have a 30-day return policy, which
means you have 30 days after receiving
your item to request a return.
}
>
Return policy
);
}
```
##### Banner
Banners in checkout UI extensions are used to communicate important messages to customers in a prominent way.
```
import {
reactExtension,
Banner,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => ,
);
function Extension() {
return (
);
}
```
### Admin UI extensions
An [admin UI extension](https://shopify.dev/docs/api/admin-extensions) is a JavaScript-based module that can hook in to client-side behaviors on any of Shopify’s first-party UI surface areas. These extensions enable your app to embed workflows and UX on core admin pages while automatically matching the Shopify admin's look and feel.
Shopify provides different “variants” of UI extension APIs that are suitable for different developers:
* [@shopify/ui-extensions](https://github.com/Shopify/ui-extensions/blob/unstable/packages/ui-extensions) lets developers use a small, strongly-typed JavaScript API for creating UI extensions.
* [@shopify/ui-extensions-react](https://github.com/Shopify/ui-extensions/blob/unstable/packages/ui-extensions-react) lets developers create UI extensions using [React](https://reactjs.org/).
#### Types of admin extensions
* **Admin actions**: Admin action extensions enable you to create transactional workflows within existing pages of the Shopify admin. Merchants can launch these extensions from the More actions menus on resource pages or from an index table's bulk action menu when one or more resources are selected.
* **Admin blocks**: Admin block extensions enable your app to embed contextual information and inputs directly on resource pages in the Shopify admin. When a merchant has added them to their pages, these extensions display as cards inline with the other resource information. With admin block extensions, merchants can view and modify information from your app and other data on the page simultaneously. To facilitate complex interactions and transactional changes, you can launch admin actions directly from an admin block.
* **Admin print actions**: Admin print actions extensions are a special form of action extension designed to let your app print documents from key pages in the Shopify admin. Unlike a typical admin action extension, these extensions are found under the Print menu on orders and product pages.
#### Create a new admin extension
To create a new extension, use Shopify CLI:
```
shopify app generate extension
```
##### Deploy an admin extension
To deploy an admin extension, run this within your app's directory:
```
npm run deploy
```
#### Example: Build an admin action
In this example, we create an extension’s UI and render it.
First, we’ll create a `shopify.extension.toml` file that targets `admin.product-details.action.render`:
```
api_version = "2025-01"
[[extensions]]
# Change the merchant-facing name of the extension in locales/en.default.json
name = "t:name"
handle = "issue-tracker-action"
type = "ui_extension"
[[extensions.targeting]]
module = "./src/ActionExtension.jsx"
# The target used here must match the target used in the module file (./src/ActionExtension.jsx)
target = "admin.product-details.action.render"
```
Next, we set the title of the page in `/locales/en.default.json`:
```
{
"name": "Create an issue"
}
```
Then, in `/src/ActionExtension.jsx` we’ll import the necessary components from Remote UI:
```
import {
reactExtension,
useApi,
TextField,
AdminAction,
Button,
TextArea,
Box,
} from "@shopify/ui-extensions-react/admin";
```
Then we’ll build out the file with the target, logic, and UI rendering:
```
import { useCallback, useEffect, useState } from "react";
import {
reactExtension,
useApi,
TextField,
AdminAction,
Button,
TextArea,
Box,
} from "@shopify/ui-extensions-react/admin";
import { getIssues, updateIssues } from "./utils";
function generateId (allIssues) {
return !allIssues?.length ? 0 : allIssues[allIssues.length - 1].id + 1;
};
function validateForm ({title, description}) {
return {
isValid: Boolean(title) && Boolean(description),
errors: {
title: !title,
description: !description,
},
};
};
// The target used here must match the target used in the extension's .toml file at ./shopify.extension.toml
const TARGET = "admin.product-details.action.render";
export default reactExtension(TARGET, () => );
function App() {
//connect with the extension's APIs
const { close, data } = useApi(TARGET);
const [issue, setIssue] = useState({ title: "", description: "" });
const [allIssues, setAllIssues] = useState([]);
const [formErrors, setFormErrors] = useState(null);
const { title, description } = issue;
useEffect(() => {
getIssues(data.selected[0].id).then(issues => setAllIssues(issues || []));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const onSubmit = useCallback(async () => {
const {isValid, errors} = validateForm(issue);
setFormErrors(errors);
if (isValid) {
// Commit changes to the database
await updateIssues(data.selected[0].id, [
...allIssues,
{
id: generateId(allIssues),
completed: false,
...issue,
}
]);
// Close the modal using the 'close' API
close();
}
}, [issue, data.selected, allIssues, close]);
return (
Create
}
secondaryAction={}
>
setIssue((prev) => ({ ...prev, title: val }))}
label="Title"
maxLength={50}
/>
);
}
```
### Customer account extensions
[Customer account UI extensions](https://shopify.dev/docs/api/customer-account-ui-extensions) let app developers build custom functionality that merchants can install at defined points on the Order index, Order status, and Profile pages in customer accounts.
Customers can navigate to their account from the online store, from order notification emails, or any custom entrypoint placed by the merchant. If the customer is not already logged in, clicking a link from an order notification email to view their order will bring them to the pre-authenticated Order status page. From there, if the customer tries to navigate to another page in their account, or tries to take an action, they’ll be prompted to log in. Once the customer logs in, they are fully authenticated and able to access all customer account pages.
Using customer account UI extensions, apps can extend the functionality of existing customer account pages, as well as, create new pages (full-page extensions).
#### Scaffold an extension
Use Shopify CLI to generate a new extension in the directory of your app:
```
npm init @shopify/app@latest
cd your-app
npm run shopify app generate extension
```
#### Configure extension targets
[Extension targets](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-01/extension-targets-overview) provide locations for customer account UI extensions to appear. Extension UIs are rendered using remote UI, a fast and secure environment for custom (non-DOM) UIs.
```
import {
reactExtension,
Banner,
useTranslate,
} from '@shopify/ui-extensions-react/customer-account';
reactExtension('customer-account.order-index.block.render', () => (
));
function App() {
const translate = useTranslate();
return {translate('welcomeMessage')};
}
```
#### Update your configuration file
When you create a customer account UI extension, the `shopify.extension.toml` file is automatically generated in your customer account UI extension directory. It contains the extension's configuration, which includes the extension name, extension targets, metafields, capabilities and settings definition.
```
api_version = "unstable"
[[extensions]]
name = "My customer account ui extension"
handle = "customer-account-ui"
type = "ui_extension"
[[extensions.targeting]]
target = "customer-account.order-status.block.render"
module = "./Extension.jsx"
```
#### Extension APIs
APIs enable customer account UI extensions to get information about the customer or related objects and to perform actions. For example, you can use APIs to retrieve previous orders of the customer so that you can offer related products as upsells. Extensions use JavaScript to read and write data and call external services, and natively render UIs built using Shopify's checkout and customer account components.
```
import {
reactExtension,
Banner,
useTranslate,
} from '@shopify/ui-extensions-react/customer-account';
reactExtension(
'customer-account.order-status.block.render',
() => ,
);
function App() {
const translate = useTranslate();
return {translate('welcomeMessage')};
}
```
#### UI components
Customer account UI extensions declare their interface using supported UI components. Shopify renders the UI natively so it's performant, accessible, and works in all of customer account supported browsers. Components are designed to be flexible, enabling you to layer and mix them to create highly-customized app extensions that feel seamless within the customer account experience. All components that will inherit a merchant's brand settings and the CSS cannot be altered or overridden.
To build customer account UI extensions, you can use checkout components, and customer account components.
```
import {
reactExtension,
BlockStack,
InlineStack,
Button,
Image,
Text,
} from '@shopify/ui-extensions-react/customer-account';
reactExtension(
'customer-account.order-status.block.render',
() => ,
);
function App() {
return (
HeadingDescription
);
}
```
### Functions
[Shopify Functions](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-01/extension-targets-overview) allow developers to customize the backend logic that powers parts of Shopify. They can be used to generate custom discounts, shipping, pickup points and more. Building Shopify Functions is similar to embedded programming - there is a focus on low latency execution and thus there are constraints on execution time.
#### How Functions work
Function extension targets inject code into the backend logic of Shopify. The key parts of a function are:
* **Function input**: The function input is a JSON object which is the result of a GraphQL input query you define. Input queries allow you to select the specific data you need for your function, such as cart line product data or metafields.
* **Function logic**: The function logic is written in any language that can compile a WebAssembly module which meets function requirements. Function templates and client libraries are available for Rust and JavaScript.
* **Function output**: The function output is a JSON document that describes the operations you'd like Shopify to carry out.
GraphQL schemas provided by Shopify specify the targets, available inputs, and expected outputs for a Functions API.
#### Example: Build a Product Discount function
In this example we build a Shopify Function that applies a 20% discount to the first item in the cart
First we create an input query to get the first item in the cart
```
query Input {
cart {
lines {
id
}
}
}
```
Then we write a Function in Rust to apply the 20% discount to the cart item
```
use shopify_function::prelude::*;
use shopify_function::Result;
use crate::run::run::output::*;
#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
fn run(input: input::ResponseData) -> Result {
let mut discounts = vec![];
let percentage = 20.0;
// Check if there are any lines in the cart
if let Some(first_line) = input.cart.lines.first() {
discounts.push(Discount {
value: Value::Percentage(Percentage {
value: Decimal(percentage),
}),
targets: vec![Target::CartLine(CartLineTarget {
id: first_line.id.clone(),
quantity: None,
})],
message: Some(format!("{}% off first item", percentage)),
});
}
Ok(FunctionRunResult {
discounts,
discount_application_strategy: DiscountApplicationStrategy::FIRST,
})
}
```
#### Example: Build a Payment Customization Function
In this example we remove a payment method if the cart total exceeds a certain amount.
Define the input query to fetch the cart total and available payment methods:
```
query Input {
cart {
cost {
totalAmount {
amount
}
}
}
paymentMethods {
id
name
}
}
```
This returns the following JSON output
```
{
"cart": {
"cost": {
"totalAmount": {
"amount": 200.0
}
}
},
"paymentMethods": [
{
"id": "gid://shopify/PaymentCustomizationPaymentMethod/1",
"name": "Cash on Delivery"
},
{
"id": "gid://shopify/PaymentCustomizationPaymentMethod/2",
"name": "Credit Card"
}
]
}
```
Then we write a Function in Rust to hide the Cash on Delivery payment method if the value is greater than $100
```
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
fn run(input: input::ResponseData) -> Result {
let no_changes = output::FunctionRunResult { operations: vec![] };
// Get the cart total from the function input, and return early if it's below 100
let cart_total: f64 = input.cart.cost.total_amount.amount.into();
if cart_total < 100.0 {
// You can use debug logs in your function
log!("Cart total is not high enough, no need to hide the payment method.");
return Ok(no_changes);
}
// Find the payment method to hide, and create a hide output operation from it
// (this will be None if not found)
let operations = input
.payment_methods
.iter()
.find(|&method| method.name.contains(&"Cash on Delivery".to_string()))
.map(|method| {
vec![output::Operation::Hide(output::HideOperation {
payment_method_id: method.id.to_string(),
})]
})
.unwrap_or_default();
// The shopify_function crate serializes your function result
Ok(output::FunctionRunResult { operations })
}
```
#### Example: Build a Delivery Option Customization Function
In this example we add a "May be delayed due to weather conditions" message to the delivery options, if the delivery address is in North Carolina.
Define the input query to fetch the provinceCode and delivery options:
```
query Input {
cart {
deliveryGroups {
deliveryAddress {
provinceCode
}
deliveryOptions {
handle
title
}
}
}
}
```
This returns the following JSON output
```
{
"cart": {
"deliveryGroups": [
{
"deliveryAddress": {
"provinceCode": "NC"
}
"deliveryOptions": [
{
"handle": "shopify-Standard-21.90",
"title": "Standard"
},
{
"handle": "shopify-Express-31.90",
"title": "Express"
},
]
}
]
}
}
```
Then we write a Function in Rust to append the message if the delivery address is in North Carolina
```
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
fn run(input: input::ResponseData) -> Result {
// The message we wish to add to the delivery option
let message = "May be delayed due to weather conditions";
let to_rename = input
.cart
.delivery_groups
.iter()
// Filter for delivery groups with a shipping address containing the affected state or province
.filter(|group| {
let state_province = group
.delivery_address
.as_ref()
.and_then(|address| address.province_code.as_ref());
match state_province {
Some(code) => code == "NC",
None => false,
}
})
// Collect the delivery options from these groups
.flat_map(|group| &group.delivery_options)
// Construct a rename operation for each, adding the message to the option title
.map(|option| output::RenameOperation {
delivery_option_handle: option.handle.to_string(),
title: match &option.title {
Some(title) => format!("{} - {}", title, message),
None => message.to_string(),
},
})
// Wrap with an Operation
.map(output::Operation::Rename)
.collect();
// The shopify_function crate serializes your function result
Ok(output::FunctionRunResult {
operations: to_rename,
})
}
```
#### Available Function APIs
* **Delivery Customization API**: Rename, reorder, and sort the delivery options available to buyers during checkout.
* **Use cases**: Hide delivery options for certain products or customers; reorder delivery options according to user preference; hide delivery options for PO Box addresses; add messaging to delivery option titles
* **Extension target**: `purchase.delivery-customization.run`
* **Order Discount API**: Create a new type of discount that's applied to all merchandise in the cart.
* **Use cases**: Money off the order subtotal; money off products on an order; tiered discount by spend.
* **Extension target**: `purchase.order-discount.run`
* **Product Discount API**: Create a new type of discount that's applied to a particular product or product variant in the cart.
* **Use cases**: Money off a product; money off a product variant; money off a cart line; buy a specific quantity of a product; buy a specific amount of a product, get a second amount at a discount.
* **Extension target**: `purchase.product-discount.run`
* **Shipping Discount API**: Create a new type of discount that's applied to one or more shipping rates at checkout.
* **Use cases**: Free shipping; a discount on shipping; a discount on specific shipping rates.
* **Extension target**: `purchase.shipping-discount.run`
* **Payment Customization API**: Rename, reorder, and sort the payment methods available to buyers during checkout.
* **Use cases**: Hide payment methods for carts with totals above or below a given value; reorder payment methods according to user preference; hide payment methods based on customer tag or country; hide and disable gift cards based on cart contents, country and more.
* **Extension target**: `purchase.payment-customization.run`
* **Cart Transform API**: Expand cart line items and update the presentation of cart line items.
* **Extension target**: `purchase.cart-transform.run`
* **Cart and Checkout Validation API**: Provide your own validation of a cart and checkout.
* **Use cases**: Use tokengating or require a customer membership at checkout; verify the age or id of a customer when they proceed through checkout; provide b2b product minimums, maximums, and multiples; provide b2b location order minimums, maximums, or credit limits; specify quantity limits in a flash sale.
* **Extension target**: `purchase.validation.run`
* **Fulfillment Constraints API**: Provide your own logic for how Shopify should fulfill and allocate an order.
* **Use cases**: Ensure that n cart line items are fulfilled from the same location; ensure that n cart line items are fulfilled from any of the locations in a list.
* **Extension target**: `purchase.fulfillment-constraint-rule.run`
* **Local Pickup Delivery Option Generator API**: Generate custom local pickup options available to buyers during checkout.
* **Use cases**: Generate local pickup options based on custom rules:
* **Extension target**: `purchase.local-pickup-delivery-option-generator.run`
* **Pickup Point Delivery Option Generator API**: Generate custom pickup point options available to buyers during checkout.
* **Use cases**: Generate pickup points based on custom rules
* **Extension target**: `purchase.pickup-point-delivery-option-generator.run`
### Webhooks
The default and recommended way to configure [webhooks](https://shopify.dev/docs/api/webhooks?reference=toml) is using `shopify.app.toml` configuration file. This will ensure that every shop on which your app is installed will provide your app with the same set of events.
You can use the following syntax for subscribing to webhooks where the URI is an endpoint that you provide. This could be an HTTP endpoint, Google Cloud PubSub endpoint, or an AWS Eventbridge endpoint.
```
[[webhooks.subscriptions]]
topics = ["orders/create"]
uri = "https://webhook.site/webhooks/o/app/orders-create"
```
If you wish to use the same URI for all webhooks just put multiple topics into the topics array. If you wish to have different endpoints depending on the topic then you should have multiple `[[webhooks.subscriptions]]` sections in your TOML. Here’s what multiple subscriptions each with a different endpoint would look like:
```
[[webhooks.subscriptions]]
topics = ["orders/create"]
uri = "https://webhook.site/webhooks/app/orders-create"
```
```
[[webhooks.subscriptions]]
topics = ["products/create"]
uri = "https://webhook.site/webhooks/p/app/orders-create"
```
You can use the GraphQL mutations for registering webhooks when you require different events per shop. For example, if you have features in your app that merchants must upgrade to enable then you might want to only receive events that are necessary when merchants upgrade. Here’s a GraphQL mutation to register a webhook on a single shop:
```
mutation WebhookSubscriptionCreate($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(topic: $topic, webhookSubscription: $webhookSubscription) {
webhookSubscription {
id
topic
apiVersion {
handle
}
format
createdAt
}
userErrors {
field
message
}
}
}
```
And that GraphQL takes the following as variable inputs:
```
{
"topic": "ORDERS_CREATE",
"webhookSubscription": {
"uri": "https://webhook.site/webhooks/app/orders-create",
"format": "JSON"
}
}
```
For that GraphQL to work your app must be authenticated and have a shop token that it can use to call that mutation on that specific shop.
### Custom data
Shopify comes with many built-in data models like products, customers, and orders. Yet often while building for the varied and diverse needs of merchants you'll need a way to customize the data in Shopify:
* Metafields to extend Shopify's resources with custom fields
* Metaobjects to create entirely new resources by making new custom objects
#### Metafields
[Metafields](https://shopify.dev/docs/apps/build/custom-data#what-are-metafields) are a flexible way to store additional details about existing Shopify resources, like products, orders, and many more. These custom fields can be almost anything, such as related products, release dates, internal approval status, or part numbers. Metafields power experiences across Shopify. In the Shopify admin, they enable features like customer segmentation, smart collections, and product taxonomy. For customers, they enhance the shopping experience through product recommendations, product swatches, and customized checkouts using Shopify Functions.
##### Unstructured metafields
Metafields serve as the foundation for extending Shopify's data model. At their core, metafields are key-value pairs that can be added to specific resources in Shopify:
* **Identifier**: Composed of both namespace (drives ownership) and key.
* **Value**: The raw value stored.
* **Type**: How value is interpreted.
The type on an unstructured metafield can vary on an instance-by-instance basis. To ensure consistency, you need a metafield definition.
**Example**
You work with a snowboard merchant who needs to store care instructions for each product. Starting simple, you add a custom.care\_guide metafield to a product by using the `productUpdate` mutation:
```
mutation {
metafieldDefinitionCreate(definition: {
name: "Care Guide",
namespace: "custom",
key: "care_guide",
description: "How to care for the product.",
type: "single_line_text_field",
ownerType: PRODUCT,
access: {
storefront: PUBLIC_READ,
},
}) {
createdDefinition {
name
namespace
key
type
access
}
}
}
```
##### Structured metafields
Metafields covered by a metafield definition, or structured metafields, have consistent types amongst other optional configurations:
* Data validation
* Permissions
* Optional features
* Conditional usage
**Example**
In this example, you'll add a definition to your snowboard merchant to ensure all products have a `custom.care_guide metafield` with a type of `single_line_text_field` that is also accessible to storefronts:
```
mutation {
metafieldDefinitionCreate(definition: {
name: "Care Guide",
namespace: "custom",
key: "care_guide",
description: "How to care for the product.",
type: "single_line_text_field",
ownerType: PRODUCT,
access: {
storefront: PUBLIC_READ,
},
}) {
createdDefinition {
name
namespace
key
type
access
}
}
}
```
#### Metaobjects
[Metaobjects](https://shopify.dev/docs/apps/build/custom-data#what-are-metaobjects) are a powerful way to create and reuse custom data structures beyond Shopify's standard resources. They exist independently and can be referenced by metafields to connect with standard resources like products, orders, and customers.
Key terms related to metaobjects:
* **Definition**: The structure outlining the fields and properties for your metaobjects.
* **Entry**: An instance of the associated definition.
Metaobject definitions, beyond defining the fields, also offer control over:
* Permissions, such as storefront visibility.
* Optional features, such as translatable fields.
**Example**
Suppose a merchant wants a `Feature` resource in Shopify. You can represent that with a new metaobject definition:
```
mutation {
metaobjectDefinitionCreate(definition: {
type: "$app:feature",
access: {
admin: MERCHANT_READ_WRITE,
storefront: PUBLIC_READ,
},
capabilities: {
translatable: { enabled: true },
},
displayNameKey: "title",
fieldDefinitions: [
{ key: "title", name: "Highlight Title", type: "single_line_text_field", required: true },
{ key: "description", name: "Description", type: "multi_line_text_field", required: true },
{ key: "creative", name: "Creative", type: "file_reference" },
]
}) {
metaobjectDefinition {
id
type
fieldDefinitions {
key
name
type
}
}
}
}
```
The merchant's products have a set of key features, so you'll also need to create a product metafield definition that references the `Feature` metaobject definition you just created:
```
mutation {
metafieldDefinitionCreate(definition: {
name: "Key features",
key: "key_features",
description: "Key features of the product.",
type: "list.metaobject_reference",
ownerType: PRODUCT,
access: {
storefront: PUBLIC_READ,
},
validation: {
metaobjectDefinitionId: "gid://shopify/MetaobjectDefinition/1",
},
}) {
createdDefinition {
namespace
key
type
}
}
}
```
With those in place, you can create `Feature` entries and reference as many as you want in a product's `key_features` metafield. These entries can be reused across products, making it easy to manage and update.
### Authentication
[Authentication](https://shopify.dev/docs/apps/build/authentication-authorization) is the process of verifying the identity of the user or the app. To keep transactions on Shopify’s platform safe and secure, all apps connecting with Shopify APIs must authenticate when making API requests.
Authorization is the process of giving permissions to apps. When an app user installs a Shopify app they authorize the app, enabling the app to acquire an access token. For example, an app might be authorized to access orders and product data in a store.
#### Types of authentication and authorization methods
The authentication and authorization methods that your app needs to use depends on the tool that you used to create your app, and the components that your app uses.
##### Authentication
* Embedded apps need to authenticate their incoming requests with session tokens.
* Apps that are not embedded need to implement their own authentication method for incoming requests.
##### Authorization
Authorization encompasses the installation of an app and the means to acquire an access token.
To avoid unnecessary redirects and page flickers during the app installation process, you should configure your app's required access scopes using Shopify CLI. This allows Shopify to manage the installation process for you.
If you aren't able to use Shopify CLI to configure your app, then your app will install as part of the authorization code grant flow. This provides a degraded user experience.
The following table outlines the supported installation and token acquisition flows for various app configurations. Whenever possible, you should create embedded apps that use Shopify managed installation and token exchange.
| Type of app | Supported installation flows | Supported token acquisition flows |
| :---- | :---- | :---- |
| Embedded app | Shopify managed installation (recommended) Installation during authorization code grant | Token exchange (recommended) Authorization code grant |
| Non-embedded app | Shopify managed installation (recommended) Installation during authorization code grant | Authorization code grant |
| Admin-created custom app | Installed upon generation in the Shopify admin | Generate in the Shopify admin |
### Deploy
When you [deploy a Shopify app](https://shopify.dev/docs/apps/build/authentication-authorization), you're making your code available to merchants. This involves:
* Moving your code from your local development environment to a hosting service
* Connecting your hosted app to Shopify through the Partner Dashboard
* Managing app extensions and configurations separately through app versions
#### Hosting and deployment options
Common hosting providers for Shopify apps:
- [Deploy to Fly.io](http://Fly.io)
- [Deploy to Render](https://render.com/docs/deploy-shopify-app)
- Manual deployment
#### How to deploy manually
##### 1\. Create an app configuration file
Create or link your app to an `app.toml file`. Note down the `SHOPIFY_API_KEY`, `SHOPIFY_API_SECRET`, and `SCOPES` values.
```
shopify app config link
shopify app env show
```
##### 2\. Build your app
The Shopify Remix app template comes set up with Vite, which can build the bundles you'll need to host your app. If your provider doesn't support Docker, then you'll need to build the app yourself.
```
npm ci
npm run build
```
##### 3\. Set up your database
Now you'll decide which database you'll use, and where to host it. There are several cloud platforms that provide specialized database containers. You can use whichever storage strategy you're most comfortable working with.
##### 4\. Set up environment variables
Apps created using Shopify CLI use environment variables for configuration. To deploy your app, you'll need to set these values manually in your hosting provider. You'll need to set the variables that you obtained previously, along with some other values, in your production environment. The following environment variables need to be provided: `SHOPIFY_APP_URL`, `SHOPIFY_API_KEY`, `SHOPIFY_API_SECRET`, `SCOPES,PORT.`
##### 5\. Deploy your app
Before running the app on your hosting provider, you'll need to update your Shopify settings by deploying your TOML file using Shopify CLI.
## Storefront customization
Storefronts on Shopify give you the power to sell the way you want. Use the tools you already know to reach your customers wherever they are.
You can customize your Shopify storefront using different approaches:
1. Using Themes and Theme App Extensions
2. Building Custom Storefronts with Hydrogen and Oxygen, powered by the Storefront API
### Liquid
Shopify themes are a package of template files, building blocks, and supporting assets. Themes shape the online store experience for merchants and their customers. You can build fast, flexible themes at scale using Liquid, Shopify's theme templating language, along with HTML, CSS, JavaScript, and JSON.
#### Liquid basics
[Liquid](https://shopify.dev/docs/api/liquid) is used to dynamically output **objects** and their properties. You can further modify that output by creating logic with **tags**, or directly altering it with a **filter**. Objects and object properties are output using one of six basic data types. Liquid also includes basic logical and **comparison operators** for use with tags.
#### Objects
Liquid [objects](https://shopify.dev/docs/api/liquid) represent variables that you can use to build your theme. Object types include, but aren't limited to:
- Store resources, such as a collection or product and its properties
- Standard content that is used to power Shopify themes, such as `content_for_header`
- Functional elements that can be used to build interactivity, such as `paginate` and `search`
Objects might represent a single data point, or contain multiple properties. Some products might represent a related object, such as a product in a collection. Some objects can be accessed globally, and some are available only in certain contexts. Refer to the specific object reference to find its access scope.
Objects, along with their properties, are wrapped in curly brace delimiters `{{ }}`.
You can find a [list of all objects](https://shopify.dev/docs/api/liquid/objects) in the Liquid reference docs.
##### Example object
The `product` object contains a property called `title` that can be used to output the title of a product:
Code:
```
{{ product.title }}
```
Data:
```
{
"product": {
"title": "Health potion"
}
}
```
Output:
```
Health potion
```
#### Tags
Liquid [tags](https://shopify.dev/docs/api/liquid/tags) are used to define logic that tells templates what to do. There are four types of tags:
* [**Conditional tags**](https://shopify.dev/docs/api/liquid/tags/conditional-tags): Define conditions that determine whether blocks of Liquid code get executed.
* [**Iteration tags**](https://shopify.dev/docs/api/liquid/tags/iteration-tags): Repeatedly run blocks of code.
* [**Theme tags**](https://shopify.dev/docs/api/liquid/tags/theme-tags): Assign or render content that’s part of your theme.
* [**Variable tags**](https://shopify.dev/docs/api/liquid/tags/variable-tags): Enable you to create new liquid variables.
Tags are wrapped with curly brace percentage delimiters `{% %}`. The text within the delimiters doesn't produce visible output when rendered.
You can find a [list of all tags](https://shopify.dev/docs/api/liquid/tags) in the Liquid reference docs.
##### Tag operators
Liquid supports basic logical and comparison operators for use with conditional tags.
| Operator | Function |
| :---- | :---- |
| \== | equals |
| \!= | does not equal |
| \> | greater than |
| \< | less than |
| \<= | less than or equal to |
| or | Condition A or Condition B |
| and | Condition A and Condition B |
| contains | Checks for strings in strings or arrays |
##### Example tag logic
In the example below, the `if` tag defines the condition to be met. If `product.available` returns `true`, then the number of available units is displayed. Otherwise, the “sold-out” message is shown.
Code:
```
{% if product.available %}
Available units: 42
{% else %}
Sorry, this product is sold out.
{% endif %}
```
Data:
```
{
"product": {
"available": true
}
}
```
Output:
```
Available units: 42
```
#### [Filters](https://shopify.dev/docs/api/liquid/filters)
Liquid filters are used to modify the output of variables and objects. To apply filters to an output, add the filter and any filter parameters within the output's curly brace delimiters `{{ }}`, preceded by a pipe character `|`. So the syntax is: `{{ input | filter }}`. Multiple filters can be used on one output. They're parsed from left to right.
You can find a [list of all filters](https://shopify.dev/docs/api/liquid/filters) in the Liquid reference docs.
##### Example filter
In the example below, product is the object, title is its property, and upcase is the filter being applied.
Code:
```
{% # product.title -> Health potion %}
{{ product.title | upcase }}
```
Data:
```
{
"product": {
"title": "Health potion"
}
}
```
Output:
```
HEALTH POTION
```
### Theme App extensions
[Theme app extensions](https://shopify.dev/docs/apps/build/online-store/theme-app-extensions) allow merchants to easily add dynamic elements to their themes without having to interact with Liquid templates or code. For example, dynamic elements can include product reviews, prices, ratings, or interactive 3D models of products. Theme app extensions can integrate with Online Store 2.0 themes, such as the default Dawn theme, which is Shopify's Online Store 2.0 reference theme.
#### Benefits
* Theme app extensions automatically expose your app in the theme editor. You can leverage the editor’s visual editing capabilities without needing to replicate them in your app.
* You can deploy your app at the same time to all online stores that use it. You also have access to versioning, and asset hosting on the Shopify CDN.
* A single set of integration logic and instructions works for all themes.
* Merchants won't need to manually edit their theme code.
#### Resources
Theme app extensions contain the following resources:
* **Blocks**: Liquid files that act as the entry point for what you want to inject in a theme. The following block types are supported:
* App blocks
* App embed blocks
* **Assets**: CSS, JavaScript, and other static app content that gets injected into themes.
* **Snippets**: Reusable Liquid snippets that can be used across multiple blocks.
#### Designing the best merchant experience
Apps built in the theme app extension framework don't edit theme code, which decreases the risk of introducing breaking changes to the theme, makes it easier to iterate on the content of the integration, and provides for a better merchant experience. Merchants can use the theme editor to configure exposed settings and add app blocks in theme sections for precise positioning in a page's layout.
#### Create a new extension
Run the following command:
```
shopify app generate extension
```
#### Configure a theme app extension
When you create a theme app extension, Shopify adds the following `theme-app-extension` directory and subdirectories to your app:
**Newly generated extension**:
```
└── extensions
└── my-theme-app-extension
├── assets
├── blocks
├── snippets
├── locales
├── package.json
└── shopify.extension.toml
```
**Populated extension:**
```
└── extensions
└── my-theme-app-extension
├── assets
│ ├── image.jpg
│ ├── icon.svg
│ ├── app-block.js
│ ├── app-block.css
│ ├── app-embed-block.js
│ └── app-embed-block.css
├── blocks
│ ├── app-block.liquid
│ └── app-embed-block.liquid
├── snippets
│ ├── app-block-snippet.liquid
│ └── app-embed-block-snippet.liquid
├── locales
| ├── en.default.json
| ├── en.default.schema.json
| ├── fr.json
| └── fr.schema.json
├── package.json
└── shopify.extension.toml
```
#### App blocks for themes
Apps that inject inline content on a page extend themes using app blocks. Merchants can add app blocks to a compatible theme section, or as wrapped app blocks that are added at the section level. Create an app block by setting the target in the schema to section.
By default, themes don't include app blocks after an app is installed. Merchants need to add the app blocks to the theme from the Apps section of the theme editor.
Use app blocks for the following types of apps:
* Apps that you want to automatically point to dynamic sources, such as product reviews apps and star ratings apps.
* Apps that merchants might want to reposition on a page.
* Apps that should span the full width of a page.
**Example**
```
App blocks let you build powerful integrations with online store themes.
{% render "app_snippet" %}
{% schema %}
{
"name": "Hello World",
"target": "section",
"enabled_on": {
"templates": ["index"]
},
"stylesheet": "app.css",
"javascript": "app.js",
"settings": [
{ "label": "Color", "id": "color", "type": "color", "default": "#000000" }
]
}
{% endschema %}
```
#### App embed blocks
Apps that don't have a UI component, or that add floating or overlaid elements, extend themes using app embed blocks. Shopify renders and injects app embed blocks before HTML `` and `