Using Deep Links Effectively in Your Shopify App Theme Extensions
Josh Boddy
Posted on February 21, 2024
Theme extensions are a fantastic way to let store customers engage with your app. They enable the use of App Blocks to seamlessly integrate your custom Liquid, HTML, JavaScript, and CSS code onto Shopify 2.0 storefronts, empowering customers to enjoy your app's features on most store pages (checkout pages being the exception). As an app developer, it's wise to leverage Deep Links to simplify the embedding of app blocks for your users. However, these links can seem a tad complex at first. Our goal today is to demystify Deep Links, making them more approachable and manageable for you.
Deep Link Structure
You can find the basic structure for Deep Links in Shopify's Developer Documentation, but let's break it down further, focusing on key areas:
https://<myshopifyDomain>/admin/themes/current/editor?template=${template}&addAppBlockId={uuid}/{handle}&target=newAppsSection
This is the standard format for a Deep Link. Let's dissect it, shall we?
-
<myshopifyDomain>
: This is your.myshopify.com
URL, not to be confused with your shop domain. Using a custom URL here might lead to errors. -
{template}
: Refers to a page template in your Theme Editor. You can add an App Block to any page with a JSON template in your code. Theproduct
page is a prime example. -
{uuid}
: This represents theSHOPIFY_EXTENSION_NAME_ID
environment variable from your project's.env
file. We'll delve into how to fetch and utilise these variables in your codebase shortly. -
{handle}
: The name of your app block's liquid file. Simply omit the.liquid
extension when using it here. -
{header|footer|aside}
optional: This specifies the section group for your app block. You can choose from the options provided or leave this out entirely. -
{sectionID}
: The ID of the section where you want to place your app block. You'll find this in the theme's JSON templates, listed undersections
.
Now that we've got the basics down, let's explore some clever ways to streamline this process and save you some hassle.
What's the Fuss?
Knowing what a Deep Link should look like is one thing, but actually using one in your app dashboard is another kettle of fish. Where do these environment variables come from? And what on earth is a Section ID, and how do you find it?
Many of these values are dynamic, varying between stores and installations. This presents a challenge for us developers, as we need to relay data from the backend to the frontend securely (keeping environment variables off the frontend), ensuring all values are accurate and functional. Here are a few strategies to tackle this...
The Solution
Our backend can serve as a powerhouse, providing an endpoint for the frontend to query and fetch the desired app block details. Let's start with the basics of string concatenation. Store the original template URL in a constant:
const deepLinkUrl = "https://<myshopifyDomain>/admin/themes/current/editor?template=${template}&addAppBlockId={uuid}/{handle}&target=newAppsSection";
Now, fill in the blanks with known values. For instance, if we're adding our app block to the product page, we'd set the template as product
:
const deepLinkUrl = "https://<myshopifyDomain>/admin/themes/current/editor?template=product&addAppBlockId={uuid}/{handle}&target=newAppsSection";
Next, wrap this in an endpoint to glean details from the current user session:
app.get("/api/deeplinks", (req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
const deepLinkUrl = "https://<myshopifyDomain>/admin/themes/current/editor?template=product&addAppBlockId={uuid}/{handle}&target=newAppsSection";
res.status(200).send(deepLinkUrl);
});
With access to the user's session info (like the current shop), we can use JavaScript's ${value}
syntax for concatenation:
app.get("/api/deeplinks", (req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
const deepLinkUrl = `https://${res.locals.shopify.session.shop}/admin/themes/current/editor?template=product&addAppBlockId={uuid}/{handle}&target=newAppsSection`;
res.status(200).send(deepLinkUrl);
});
Adding ${res.locals.shopify.session.shop}
to the URL dynamically fills in the shop's URL making the request.
Next, introduce the dotenv
package to your Shopify app with npm install dotenv --save
. Import it at the top of your file:
import "dotenv/config";
This allows us to access those crucial environment variables with process.env.VARIABLE_NAME
, adhering to the best practice of uppercase variable names:
import "dotenv/config";
app.get("/api/deeplinks", (req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
const deepLinkUrl = `https://${res.locals.shopify.session.shop}/admin/themes/current/editor?template=product&addAppBlockId=${process.env.SHOPIFY_EXT_NAME_ID}/{handle}&target=newAppsSection`;
res.status(200).send(deepLinkUrl);
});
After deploying your app (using npm run deploy
), update the environment variable name in your code to match the one in your newly created .env
file.
Finally, replace the {handle}
placeholder with your .liquid
file's name to complete your Deep Link:
import "dotenv/config";
app.get("/api/deeplinks", (req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
const deepLinkUrl = `https://${res.locals.shopify.session.shop}/admin/themes/current/editor?template=product&addAppBlockId=${process.env.SHOPIFY_EXT_NAME_ID}/APP_BLOCK_LIQUID_FILE_NAME&target=newAppsSection`;
res.status(200).send(deepLinkUrl);
});
Voilà! You've crafted a straightforward endpoint to fetch your Deep Link from the backend, fully populated with all necessary data. But how do you put this to use?
Simple.
In your frontend, make a request to your backend to retrieve this data, perhaps using fetch
in a React template or a loader
function in Remix. Then, embed this URL in a Button
component. Clicking this button will effortlessly embed your app block onto a store, bypassing the need for intricate configurations or data manipulation.
Going Further
Feeling adventurous? Here's a bonus challenge: Create a list of deep link URLs for each app block you develop.
Consider using Node's fs
library to scan your blocks
folder within the theme app extension directory. Generate a new URL for each file, returning a list in a JSON response to the frontend. Use the .map
function to craft a button for each URL, allowing you to add all your app blocks to the UI as you create them, rather than manually crafting a new button and request for each new block. But that's a story for another day. Let me know how you fare and don't hesitate to reach out with questions.
Happy coding!
- Josh
Posted on February 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.