Before you start integrating your app, determine the method required for integrating an app with an online store by verifying whether its published theme supports app blocks. > Tip: > If a theme doesn't currently support app blocks, and you maintain the theme or a store where the theme is used, then you can also consider [migrating the theme to support app blocks](/docs/storefronts/themes/os20#migrating-to-online-store-2-0). ## Verification criteria To verify whether a theme supports app blocks, you need to determine the following: * Whether the template where your app is injected supports [JSON](/docs/storefronts/themes/architecture/templates/json-templates). * The main section in the template. * Whether the section where your app is injected has a block of type [`@app`](/docs/storefronts/themes/architecture/blocks/app-blocks) in its schema. For an example, refer to Shopify's sample [product reviews app](https://github.com/Shopify/product-reviews-sample-app). To see a theme that uses JSON templates and supports app blocks in several sections, refer to Shopify's reference theme, [Dawn](/docs/storefronts/themes/tools/dawn). ## Requirements You've requested the `read_themes` [GraphQL Admin API access scope](/docs/api/usage/access-scopes#authenticated-access-scopes). ## Verify support for app blocks You can use the following code to verify that a theme supports app blocks. The code uses [Shopify's Node library](/docs/api#api-libraries), which provides support for JavaScript apps to access the [GraphQL Admin API](/docs/api/admin-graphql). ```js import {shopifyApi} from '@shopify/shopify-api'; // Create library object const shopify = shopifyApi({ // The next four values are typically read from environment variables for added security apiKey: 'APIKeyFromPartnerDashboard', apiSecretKey: 'APISecretFromPartnerDashboard', scopes: ['read_products'], hostName: 'cloudflare-tunnel-address', ... }); // Specify the name of the template that the app will integrate with const APP_BLOCK_TEMPLATES = ["product", "collection", "index"]; const getMainThemeId = ` query getMainThemeId { themes(first: 1, roles: [MAIN]) { nodes { id } } }`; let response = await client.request(getMainThemeId); const themeId = response.data.themes.nodes[0].id; // Retrieve the JSON templates that we want to integrate with const getFilesQuery = ` query getFiles($filenames: [String!]!, $themeId: ID!) { theme(id: $themeId) { files(filenames: $filenames) { nodes { filename body { ... on OnlineStoreThemeFileBodyText { content } ... on OnlineStoreThemeFileBodyBase64 { contentBase64 } } } } } } `; response = await client.request(getFilesQuery, { variables: { themeId: themeId, filenames: APP_BLOCK_TEMPLATES.map((f) => `templates/${f}.json`), }, }); const jsonTemplateFiles = response.data.theme.files.nodes; if ( jsonTemplateFiles.length > 0 && jsonTemplateFiles.length === APP_BLOCK_TEMPLATES.length ) { console.log("All desired templates support sections everywhere!"); } else if (jsonTemplateFiles.length) { console.log( "Only some of the desired templates support sections everywhere.", ); } const jsonTemplateData = jsonTemplateFiles.map((file) => { return { filename: file.filename, body: jsonc_parse(file.body.content) }; }); // Retrieve the body of JSON templates and find what section is set as `main` const templateMainSections = jsonTemplateData .map((file) => { const main = Object.entries(file.body.sections).find( ([id, section]) => id === "main" || section.type.startsWith("main-"), ); if (main) { return "sections/" + main[1].type + ".liquid"; } }) .filter((section) => section); const sectionFiles = await client.request(getFilesQuery, { variables: { themeId: themeId, filenames: templateMainSections }, }); const sectionsWithAppBlock = sectionFiles.data.theme.files.nodes .map((file) => { let acceptsAppBlock = false; const match = file.body.content.match( /\{\%\s+schema\s+\%\}([\s\S]*?)\{\%\s+endschema\s+\%\}/m, ); const schema = jsonc_parse(match[1]); if (schema && schema.blocks) { acceptsAppBlock = schema.blocks.some((b) => b.type === "@app"); } return acceptsAppBlock ? file : null; }) .filter((section) => section); if ( jsonTemplateData.length > 0 && jsonTemplateData.length === sectionsWithAppBlock.length ) { console.log( "All desired templates have main sections that support app blocks!", ); } else if (sectionsWithAppBlock.length) { console.log("Only some of the desired templates support app blocks."); } else { console.log("None of the desired templates support app blocks"); } ``` ## Specify the method for integrating the app
App integration methods
App block support Integration method
Yes Skip requests made to the ScriptTag object.
No

Make requests to the ScriptTag object, or provide instructions to add code using a custom Liquid section or block, or the custom CSS setting.

## Provide onboarding instructions You need to provide merchants with post-installation instructions about onboarding your app. To learn more about writing effective help documentation, refer to [Help documentation](https://polaris.shopify.com/content/help-documentation) in Shopify Polaris and to the theme app extension [onboarding instruction guidelines](/docs/apps/build/online-store/theme-app-extensions/ux#merchant-onboarding).
App block support and onboarding instruction examples
App block support Onboarding
Yes

Provide merchants with instructions about using the app block.

For example, document instructions for adding the app block to the theme.

No

Provide merchants with instructions about any configurations required to integrate the app.

For example, document instructions for copying and pasting a code snippet to a page.