Skip to content

Introducing the New Shopify and Next.js 13 Starter Kit

Screenshot 2023-08-02 180048


We are delighted to announce our brand new Shopify and Next.js 13 starter kit to our kits collection. This Starter kit is amazing to help you and your team quickly bootstrap a custom Shopify storefronts powered by Next.js 13 App Router since it has almost every feature you need for production set up and running!

Why did we build it?

At This Dot Labs, we work with a variety of clients, from big corporations to startups. Recently, we realized that we have a lot of clients who use Shopify that want to build custom stores using Next.js to benefit from its features. Additionally, we know that Hydrogen Shopify has only been limited to Remix since they acquired it. We had a lot of a hard time refilling the gap Hydrogen left to implement its features from scratch in our client projects. And from here, we realized that we need to build a starter kit to:

  • Help our teams build custom storefronts using Next.js Quickly
  • Reduce the cost for our clients

There were a lot of Next.js Shopify starters out there, but they were very basic and didn’t have all the features our teams needed, unlike the Official Hydrogen Starter Example which is complete. So, we decided to convert the Hydrogen starter from Remix/Hydrogen to Next.js, and use the power of the App Router and React Server Components.

Tech Stack

We used in this project:

Why Shopify

Shopify is a great e-commerce platform. They have a lot of experience in this field since 2006, and now over 4.12 million e-commerce sites are built with Shopify. (Source: Shopify is used by online sellers in over 175 countries around the world, with 63% of Shopify stores estimated to be based in the US.

Why Next.js 13

As we mentioned above, we have a lot of clients who already use Next.js and it’s a very popular framework in the market for its flexibility, scalability, and diverse collection of features.


Since this starter kit is based on the official Shopify Hydrogen template, it has all of it’s features as well including:

  • Light/Dark themes support
  • Authentication system with login, register, forgot password, and account pages
  • Supports both Mobile/Desktop screens
  • Has built-in infinity scroll pagination, built with Server Components
  • State management using Zustand
  • Variety of custom fonts
  • All components have been built with Tailwind
  • Static analysis testing tools pre-configured like TypeScript, Eslint, Prettier, and a lot of useful extensions
  • Storybooks for the consistency of the system design
  • GitHub Pull requests and release templates
  • Shopify analytics integrated with the kit
  • Great performance and SEO scores
  • And finally, incompatible performance tested by both Lighthouse and PerfBuddy


This kit is running at top performance! It has amazing performance, SEO, and accessibility scores.

We tested it using PerfBuddy, and the results are incredible on both Desktop and Mobile. Here is the full report.

Screenshot 2023-08-02 181153

Note: PerfBuddy is an incredible free tool to analyze the performance of your web apps accurately. Check it out!

Also, the results from Lighthouse are pretty fascinating:

Screenshot 2023-08-02 181411

When do I use this kit?

This starter kit is great for starting new custom storefronts rapidly, especially for startups to save time and money. Also, it can be used to migrate from the old Next.js storefront to the new App router directory as it has all of the examples your team will need to integrate Shopify in Next.js 13


We are very excited to share this starter kit with you, and we hope you find it useful for your projects. We are looking forward to hearing your feedback and suggestions to improve it, and add more features to it. Also, we are planning to add more starter kits to our collection, so stay tuned for more updates!

This Dot Labs is a development consultancy that is trusted by top industry companies, including Stripe, Xero, Wikimedia, Docusign, and Twilio. This Dot takes a hands-on approach by providing tailored development strategies to help you approach your most pressing challenges with clarity and confidence. Whether it's bridging the gap between business and technology or modernizing legacy systems, you’ll find a breadth of experience and knowledge you need. Check out how This Dot Labs can empower your tech journey.

You might also like

Setting Up a Shopify App and Getting Order Data cover image

Setting Up a Shopify App and Getting Order Data

Today, we are going on an adventure! We’re starting a three-part guide on creating a Shopify app and updating a customer's order with tracking information. For this article, it's assumed that you already have a Shopify store. If you want to skip ahead and look at the code, it can be found here. To start us off, we’ll use the Shopify Create app and then follow it up with retrieving customer orders. Shopify Create app Getting started with the Shopify Create app will be a quick and easy process. We’ll start by navigating to a directory where we want to create our app and run the following `shell yarn create @shopify/app. ` We’ll be greeted by a few different prompts asking for the project name, and building your app selection. Success! Now let's navigate into our new directory and do a yarn dev, and we’ll get a few options. We’ll choose to create it as a new app, add an app name, config name, and select the store we want to use. With that out of the way, we’ll open the preview by pressing the p button. It should automatically open it up in a window and show us an app install screen where we will click the Install app button. This should redirect us to our Remix app template screen Install__ Template__ Perfect! We now have a basic Shopify Create app up and running. Next, we will move on to adding in the ability to retrieve our customer orders. Orders query Alright, it’s customer order time! We’re going to be leaving the template mostly as is. We are going to focus on adding the call and verifying the data comes back. We’ll navigate over to our app/routes/app.index.jsx file and start changing the loader function. Start by removing: `js const { session } = await authenticate.admin(request); return json({ shop:"", "") }); ` And replacing it with: `js const { admin } = await authenticate.admin(request); const response = await admin.graphql( query getOrders { fulfillmentOrders (first: 30) { edges { node { requestStatus createdAt updatedAt status fulfillAt fulfillBy fulfillments (first: 10) { edges { node { createdAt deliveredAt displayStatus estimatedDeliveryAt id inTransitAt legacyResourceId name status totalQuantity updatedAt trackingInfo { company number url } } } } order { id name note createdAt displayFulfillmentStatus } assignedLocation { address1 address2 city countryCode name phone province zip } destination { address1 address2 city company countryCode email firstName lastName phone province zip } } } } } ); const responseJson = await response.json(); console.log("responseJson", responseJson); const orders = responseJson?.data?.fulfillmentOrders?.edges?.map( (edge) => edge.node ) || [[]]; return json({ orders: orders, }); ` Next, swap Wrap change { shop } in: `js const { shop } = useLoaderData(); ` With ` Orders ` Follow that up with changing `js useEffect(() => { if (productId) {"Product created"); } }, [productId]); ` To `js useEffect(() => { if (orders) {"Orders received"); } }, [orders]); ` Then, we’ll remove the View product button that has the old shop variable in it. When you go back and look at your application, you should see the Error: Access denied for fulfillmentOrders field. This is due to scopes that we haven’t updated. To fix this, we’ll head over to our file and replace `js scopes = “writeproducts” ` with `toml scopes = "writeproducts,read_orders,read_assigned_fulfillment_orders,read_merchant_managed_fulfillment_orders,read_third_party_fulfillment_orders,write_assigned_fulfillment_orders,write_merchant_managed_fulfillment_orders,write_third_party_fulfillment_orders" ` Here is what you should now have: `toml name = "blog-test" clientid = "" applicationurl = "" embedded = true [accessscopes] Learn more at scopes = "writeproducts,read_orders,read_assigned_fulfillment_orders,read_merchant_managed_fulfillment_orders,read_third_party_fulfillment_orders,write_assigned_fulfillment_orders,write_merchant_managed_fulfillment_orders,write_third_party_fulfillment_orders" [auth] redirecturls = [ "", "", "" ] [webhooks] apiversion = "2023-07" [pos] embedded = false [build] automaticallyupdate_urls_on_dev = true devstore_url = "" ` We’ll now do another yarn dev` which will tell us that our scopes inside the TOML don’t match the scopes in our Partner Dashboard. To fix this, we simply need to run: `shell yarn shopify app config push ` And then we’ll be prompted to confirm our changes with the difference shown. It will give us a success response, and now we can do another yarn dev to look at our application. Doing so brings us back to our old friend the app install page. Only this time it’s telling us to update the app and redirecting us back to the application page. Huh, seems like we’re getting a new error this time. For some reason, the app is not approved to access the FulfillmentOrder object. No worries, follow the link in that message. It should be[partnerId]/apps/[appId]/customerdata Here, we select the Protected customer data and just toggle Store management and save. After this, go down to the Protected customer fields (optional) and do the same thing for Name, Email, Phone, and Address. With that all said and done, we’ll exit the page. After that, go back to our application, and refresh it. Tada! It works! We will see a toast notification at the bottom of the screen that says Orders received and in our terminal console, we will see our orders returning. Conclusion That was an exciting start to our three-part adventure. We covered a lot, from setting up a Shopify app to getting our orders back and everything up and running! Next time, we’ll be digging into how to get our fulfillment ids, which will be needed to update a customer's order with tracking information....

Setting Up a Shopify App: Updating Customer Orders with Tracking Info  cover image

Setting Up a Shopify App: Updating Customer Orders with Tracking Info

Today, we are wrapping up our adventure! Last time we learned about retrieving fulfillment IDs, but this time, we encounter the final boss: updating customer orders with tracking information. Now, if we have any new adventurers with us, I recommend heading over here to prep yourself for the encounter ahead. If you just need a recap of our last session, you can head over here. Alternatively, if you just want the code from last time that can be found here. If you want to skip ahead and look at the code it can be found here. With that all said we’re off to battle with updating customer orders with tracking information! Body We’re gonna start by heading over to our app/routes/app.index.jsx file, and grabbing the code found in the loader function. We’ll be moving that to our action function so we can add our post call. We’ll completely replace the existing action function code, and because of that, we need to make a couple of tweaks to the code base. We’re going to remove anything that has a reference to actionData?.product` or `productId`. Now what we need to add the call, which will allow us to update customer orders with tracking information. We’ll be placing it under our fulfillment ID loop. Here is a general example of what that call will look like. `js const fulfillment = new{ session: session, }); ` This is a good start as we now have our fulfillment information and get to add a few things to it. We’ll start off by adding our fulfillment ID and then our fulfillment tracking info. `js fulfillment.lineitems_by_fulfillment_order = [ { Fulfillmentorder_id: , }, ]; fulfillment.trackinginfo = { company: , number: , }; ` Awesome! Now we have given the fulfillment ID and tracking info we need to the fulfillment object, but we need to do one more thing for that to update. Thankfully, it’s a small thing and that’s to save it. `js await{ update: true, }); ` Now, the above will work wonderfully for a single order, but based on our prior adventurers, we had multiple ids for orders that needed to be completed. So our next step is to loop over our fulfillment object. Though before we do that, here is what the current code should look like: `js const fulfillment = new{ session: session, }); fulfillment.lineitems_by_fulfillment_order = [ { Fulfillmentorder_id: , }, ]; fulfillment.trackinginfo = { company: , number: , }; await{ update: true, }); ` Before we go to loop over this, we’re going to add a small change to fulfillmentIds. We’re going to create a new variable and add the company and tracking number information. So above the fulfillment variable, we will add this: `js const fulfillmentIdsComplete = => { = "USPS"; item.trackingNumber = "1Z001985YW99744790"; }); ` Perfect! Now for the looping, we’ll just wrap it in a for of loop: `js for (const fulfillmentIdComplete of fulfillmentIdsComplete) { const fulfillment = new{ session: session, }); fulfillment.lineitems_by_fulfillment_order = [ { Fulfillmentorder_id:, }, ]; fulfillment.trackinginfo = { company:, number: fulfillmentIdComplete.trackingNumber, }; await{ update: true, }); } ` Now that the loop is set up, we will be able to go through all of the orders and update them with the shipping company and a tracking number. So we’ll run a yarn dev, and navigate to the app page by pressing the p button. We should see our template page, and be able to click on the Generate a product button. Now we’ll navigate to our order page and we should see all open orders set to fulfilled. Conclusion Here we are. At the end of our three part saga, we covered a fair share of things in order to get our customer orders tracking information added, and can now take a long rest to rejuvenate....

How to Optimize GraphQL Performance with Redis cover image

How to Optimize GraphQL Performance with Redis

Intro GraphQL is a great way to build backend applications. It simplifies the project’s structure and scales easily, but there are some challenges that come with it. One of the biggest is how to optimize GraphQL at scale. In this post, we will show you how to use Redis as an optimization tool for your GraphQL servers. What is Redis? Redis is an open-source database that can be used with any programming language. It is well-suited for large-scale applications. It’s a key-value store that can be used to store and retrieve data. It is designed to be fast, with no single point of failure. It also provides a high degree of concurrency, which means it's possible for multiple clients to access the same data at the same time. Redis is also lightweight enough that you can run it in the cloud. Why GraphQL? GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. Why Redis with GraphQL? Redis can be your best friend when it comes to optimizing the performance of GraphQL. First, Redis provides a powerful system that helps you to cache queries and results so that they can be reused. This is crucial because there's no way to predict how often you'll need to run a query on a serverless architecture. Sometimes you use a 3rd party API that is slow, or there are request limits, or even the local databases could take a quite long time to return the data. If you're creating tons of different queries every day, this can add up quickly! Different caching strategies There are two main caching strategies that you can use with GraphQL and Apollo Server: 1. Caching the entire response (use Redis as a cache)__ Caching the whole response is the most straightforward way to cache your GraphQL queries. When you cache the entire response, you're essentially caching the entire query, including all of the fields that are returned by the query. This is a great option if you're only interested in caching the data that are returned by the query, and you don't need to worry about caching any of the other data that is returned by the query, or if you have a repeatable query for different users. Example of in-memory cache: 2. Caching individual fields (use Redis as a data store)__ This is a more proper way to cache your GraphQL queries. It's also a more complex way to cache queries in Apollo Server. When you cache individual fields, you're caching the individual fields that are returned by the query. This is a great option if you're only interested in caching the data that are returned by the query, and you don't need to worry about caching any of the other data that are returned by the query. What not to cache? Redis is not built for large data. If you're storing critical business data, you're going to need a more traditional data solution. If you're looking for a way to store complex data queries, look elsewhere. Redis is designed to be simple and fast, but that doesn't mean it's ready for just about anything. Even if you think your data could grow into a large enough set that it would benefit from relational databases, remember that Redis does not have support for building relational databases from scratch. It has no support for tables or relationships or any other kind of constraints that would be required if your data was stored in a relational database. Conclusion In this post, we showed you how to use Redis as an optimization tool for your GraphQL. We also showed you how to use Redis as a cache and as a data store. We hope you found this post helpful! Also, check out our GraphQL Serverless Contentful starter kit on If you have any questions or comments, please feel free to send them to us by email at Thanks for reading!...

Being a CTO at Any Level: A Discussion with Kathy Keating, Co-Founder of CTO Levels cover image

Being a CTO at Any Level: A Discussion with Kathy Keating, Co-Founder of CTO Levels

In this episode of the engineering leadership series, Kathy Keating, co-founder of CTO Levels and CTO Advisor, shares her insights on the role of a CTO and the challenges they face. She begins by discussing her own journey as a technologist and her experience in technology leadership roles, including founding companies and having a recent exit. According to Kathy, the primary responsibility of a CTO is to deliver the technology that aligns with the company's business needs. However, she highlights a concerning statistic that 50% of CTOs have a tenure of less than two years, often due to a lack of understanding and mismatched expectations. She emphasizes the importance of building trust quickly in order to succeed in this role. One of the main challenges CTOs face is transitioning from being a technologist to a leader. Kathy stresses the significance of developing effective communication habits to bridge this gap. She suggests that CTOs create a playbook of best practices to enhance their communication skills and join communities of other CTOs to learn from their experiences. Matching the right CTO to the stage of a company is another crucial aspect discussed in the episode. Kathy explains that different stages of a company require different types of CTOs, and it is essential to find the right fit. To navigate these challenges, Kathy advises CTOs to build a support system of advisors and coaches who can provide guidance and help them overcome obstacles. Additionally, she encourages CTOs to be aware of their own preferences and strengths, as self-awareness can greatly contribute to their success. In conclusion, this podcast episode sheds light on the technical aspects of being a CTO and the challenges they face. Kathy Keating's insights provide valuable guidance for CTOs to build trust, develop effective communication habits, match their skills to the company's stage, and create a support system for their professional growth. By understanding these key technical aspects, CTOs can enhance their leadership skills and contribute to the success of their organizations....