{"id":14957392,"url":"https://github.com/ndimatteo/hull","last_synced_at":"2025-04-06T10:14:06.879Z","repository":{"id":39644774,"uuid":"305485770","full_name":"ndimatteo/HULL","owner":"ndimatteo","description":"💀 Headless Shopify Starter – powered by Next.js + Sanity.io","archived":false,"fork":false,"pushed_at":"2023-11-03T13:57:37.000Z","size":316124,"stargazers_count":1413,"open_issues_count":7,"forks_count":178,"subscribers_count":20,"default_branch":"main","last_synced_at":"2025-04-06T10:13:59.962Z","etag":null,"topics":["ecommerce","framer-motion","headless","jamstack","next-js","nextjs","react","sanity","shopify","starter","starter-template","tailwindcss","vercel"],"latest_commit_sha":null,"homepage":"https://hull.dev","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ndimatteo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"ko_fi":"ndimatteo"}},"created_at":"2020-10-19T19:00:26.000Z","updated_at":"2025-03-30T04:51:17.000Z","dependencies_parsed_at":"2024-08-01T22:52:16.509Z","dependency_job_id":null,"html_url":"https://github.com/ndimatteo/HULL","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndimatteo%2FHULL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndimatteo%2FHULL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndimatteo%2FHULL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndimatteo%2FHULL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ndimatteo","download_url":"https://codeload.github.com/ndimatteo/HULL/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247464226,"owners_count":20942970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecommerce","framer-motion","headless","jamstack","next-js","nextjs","react","sanity","shopify","starter","starter-template","tailwindcss","vercel"],"created_at":"2024-09-24T13:14:50.202Z","updated_at":"2025-04-06T10:14:06.858Z","avatar_url":"https://github.com/ndimatteo.png","language":"JavaScript","funding_links":["https://ko-fi.com/ndimatteo"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"public/HULL-Logo.svg\" align=\"center\" height=\"100\" /\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eHeadless Shopify starter built on \u003ca href=\"https://nextjs.org\"\u003eNext.js\u003c/a\u003e\u003c/strong\u003e 🤘 \u003cbr /\u003e\n  \u003cstrong\u003eHeadless CMS powered by \u003ca href=\"https://sanity.io\"\u003eSanity.io\u003c/a\u003e\u003c/strong\u003e ⚡\u003cbr /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://hull.dev\"\u003e\n    \u003cimg src=\"https://img.shields.io/static/v1?label=\u0026message=View%20Demo\u0026style=for-the-badge\u0026color=black\u0026logo=vercel\" /\u003e\n  \u003c/a\u003e\n  \u003cbr /\u003e\n  \u003ca href=\"https://www.sanity.io/create?template=ndimatteo/HULL\"\u003e\n    \u003cimg src=\"https://img.shields.io/static/v1?label=Sanity\u0026message=Create%20Project\u0026style=for-the-badge\u0026color=156dff\u0026labelColor=black\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#-features\"\u003eFeatures\u003c/a\u003e •\n  \u003ca href=\"#-tours\"\u003eTours\u003c/a\u003e •\n  \u003ca href=\"#-automatic-set-up\"\u003eSet Up\u003c/a\u003e •\n  \u003ca href=\"#-spin-up\"\u003eSpin Up\u003c/a\u003e •\n    \u003ca href=\"#-deployment\"\u003eDeployment\u003c/a\u003e •\n  \u003ca href=\"#-extrastips\"\u003eExtras\u003c/a\u003e\n\u003c/p\u003e\n\u003cbr /\u003e\n\n\u003cbr /\u003e\n\n# ✨ Features\n\n- Utility-first CSS with [Tailwind CSS](https://tailwindcss.com)\n- Animations powered by [Framer Motion](https://www.framer.com/motion/)\n- Cart powered by [Shopify Buy SDK](https://www.npmjs.com/package/shopify-buy)\n- Real-time inventory check for products using [SWR](https://swr.vercel.app)\n- Customizable Filtering \u0026 Sorting for product collections\n- Klaviyo waitlist form for out-of-stock products\n- Klaviyo newsletter form with opt-in field\n- Dynamic Page Routes for custom page creation\n- Automatic `Sitemap.xml` generation\n- Automatic `robots.txt` generation\n- Automatic 301 Redirects from Sanity\n- Live Preview content directly from Sanity\n- Modern Image component using Sanity's Hotspot, Crop, and automatic WEBP format\n- Modular page content for all pages, including dynamic grid layouts\n- Customizable Promotion Banner\n- Customizable Cookie Notice\n- Accessibility features:\n  - ARIA Landmark Roles\n  - Default focus states preserved for keyboard navigation\n  - Correctly trap focus for drawers with [focus-trap-react](https://www.npmjs.com/package/focus-trap-react)\n  - Roving tabindex for radio buttons\n  - Input-based quantity counters\n  - Required `alt` text for all images\n  - \"Skip to Content\" link\n- SEO features:\n  - Page-level SEO/Share settings with previews\n  - Fallback Global SEO/Share settings\n  - Automatic JSON-LD Schema markup for products\n\n### Shopify Integration Features\n\n- Automatically syncs products from Shopify into Sanity\n- Custom action to sync product cart thumbnails back to Shopify from Sanity\n- Tracks product status _(draft/published)_ from Shopify to help control visibility while editing\n- Deleted products and variants are preserved and flagged in Sanity\n- Updates the URL on variant changes while keeping a clean history stack\n- Vanity shop URL masking\n- Global Cart with access to all variant data for line items\n- Supports Single Variant products out of the box\n- Product photo galleries with variant granularity\n- Dynamic `/shop` collection page\n- Custom collection pages\n- Ability to surface a variant option on product cards\n\n\u003cbr /\u003e\n\n# 🎧 Tours\n\nStill not sold? Here's some videos to get you psyched:\n\n**Famous 5-Minute Setup™ - `Coming Soon`** \u003cbr /\u003e\n_From sync to sale, watch me spin up a fresh storefront in under 5 minutes!_\n\n**Explore the file Structure - `Coming Soon`** \u003cbr /\u003e\n_In-depth look at the file structure, naming conventions, and logic under the hood_\n\n**Setting up your first Product - `Coming Soon`** \u003cbr /\u003e\n_Explore the Product settings within Sanity and how to properly setup PDP pages and PLP cards_\n\n**Connecting to Klaviyo and testing your Forms - `Coming Soon`** \u003cbr /\u003e\n_Learn how to quickly connect Klaviyo to utilize product waitlist and newsletter forms_\n\n**Setup your first Vercel deployment - `Coming Soon`** \u003cbr /\u003e\n_Using the Sanity Vercel Deploy plugin, see how easy it is to empower your clients to trigger deploys_\n\n\u003cbr /\u003e\n\n# 🔥 Automatic Set Up\n\nQuickly [deploy as a Sanity Starter](https://www.sanity.io/create?template=ndimatteo/HULL) on [Vercel](https://vercel.com) with a pre-populated store! Once deployed, simply follow step 2 and 3 below to connect your Shopify store.\n\n\u003e **Warning** \u003cbr /\u003eYou should delete the demo products once you connect your own Shopify account. Demo products will not function properly as they are not part of _your Shopify store_. Additionally, any existing products in your Shopify store will not automatically sync into Sanity. To trigger a sync, you must make a change to your existing product(s) in Shopify first.\n\n\u003cbr /\u003e\n\n# 💀 Manual Set Up\n\nClone this repository from your GitHub account with the [Use this template](https://github.com/ndimatteo/HULL/generate) button\n\n### 1) Sanity\n\n1. **Initialize and build the Sanity Studio**\n   - Make sure you have the [Sanity CLI](https://www.sanity.io/docs/getting-started-with-sanity-cli) installed globally first\n   - `yarn \u0026\u0026 sanity init` in the `/studio` folder\n   - During Sanity's initalization it will warn you that the Sanity Studio is already configured. Type `Y` and hit `enter` to reconfigure it to your own project\n   - When it asks you what dataset configuration to use, go with the `default`\n2. **Add CORS Origins to Sanity project**\n   - Visit [manage.sanity.io](https://manage.sanity.io) and go to [your-project] \u003e API \u003e \"CORS origins\"\n   - Add your Studio URLs **_with_** credentials: `http://localhost:3333` and `[subdomain].sanity.studio`\n   - Add your front-end URLs **_without_** credentials: `http://localhost:3000` and `https://[subdomain].vercel.app`\n\n### 2) Shopify Storefront Access\n\n1. **Allow custom app development in Shopify**\n   - Go to \"Settings\" _(bottom left)_ \u003e \"Apps and sales channels\" \u003e \"Develop apps\" _(top right)_\n   - click \"Allow custom app development\"\n2. **Create a custom app in Shopify**\n   - Go to \"Settings\" _(bottom left)_ \u003e \"Apps and sales channels\" \u003e \"Develop apps\" _(top right)_\n   - click \"Create an app\"\n   - Give this a relevant App name, I prefer: \"Headless Storefront\", so it's clear what it's being used for\n   - Use your dev account as the App developer to know when there are issues\n3. **Configure Admin API scopes**\n   - Configuration \u003e Admin API integration \u003e \"Configure\"\n   - Check the following boxes for the \"Products\" scope: `write_products` and `read_products`\n4. **Configure Storefront API scopes**\n   - Configuration \u003e Storefront API integration \u003e \"Configure\"\n   - Check the following boxes for the \"Products\" scope: `unauthenticated_read_product_listings` and `unauthenticated_read_product_inventory`\n   - Check the following boxes for the \"Checkout\" scope: `unauthenticated_write_checkouts` and `unauthenticated_read_checkouts`\n5. **Install the App**\n\n### 3) Shopify Webhooks\n\n1. Go to \"Settings\" _(bottom left)_ -\u003e \"Notifications\" -\u003e \"Webhooks\" _(very bottom)_\n2. add the following webhooks with the (Latest) stable API version:\n   - Product creation - `[live-domain]/api/shopify/product-update`\n   - Product update - `[live-domain]/api/shopify/product-update`\n   - Product deletion - `[live-domain]/api/shopify/product-delete`\n     \u003e **Warning** \u003cbr /\u003eYou have to use a real, live domain name (not localhost!). Be sure to use your Vercel project URL during development, and then switch to the production domain once live. You may not know your Vercel project URL until you deploy, feel free to enter something temporary, but make sure to update this once deployed!\n\n### 4) NextJS\n\n1. `yarn` in the project root folder on local\n2. Create an `.env.local` file in the project folder, and add the following variables:\n\n```\nNEXT_PUBLIC_SANITY_PROJECT_DATASET=production\nNEXT_PUBLIC_SANITY_PROJECT_ID=XXXXXX\nSANITY_API_TOKEN=XXXXXX\nSANITY_STUDIO_PREVIEW_SECRET=XXXXXX\n\nNEXT_PUBLIC_SHOPIFY_STORE_ID=XXXXXX\nNEXT_PUBLIC_SHOPIFY_STOREFRONT_API_TOKEN=XXXXXX\nSHOPIFY_ADMIN_API_TOKEN=XXXXXX\nSHOPIFY_WEBHOOK_INTEGRITY=XXXXXX\n\n// Needed for Klaviyo forms:\nKLAVIYO_API_KEY=XXXXXX\n\n// Needed for Mailchimp forms:\nMAILCHIMP_API_KEY=XXXXXX-usX\nMAILCHIMP_SERVER=usX\n\n// Needed for SendGrid forms:\nSENDGRID_API_KEY=XXXXXX\n```\n\n3. Update all the `XXXXXX` values, here's where to find each:\n\n- `NEXT_PUBLIC_SANITY_PROJECT_ID` - You can grab this after you've initalized Sanity, either from the `studio/sanity.json` file, or from your Sanity Manage dashboard\n- `SANITY_API_TOKEN` - Generate an API token for your Sanity project. Access your project from the Sanity Manage dashboard, and navigate to: \"Settings\" -\u003e \"API\" -\u003e \"Add New Token\" button. Make sure you give `read + write` access!\n- `SANITY_STUDIO_PREVIEW_SECRET` - A unique string of your choice. This is used to confirm the authenticity of \"preview mode\" requests from the Sanity Studio\n- `NEXT_PUBLIC_SHOPIFY_STORE_ID` - This is your Shopify store ID, it's the subdomain behind `.myshopify.com`\n- `NEXT_PUBLIC_SHOPIFY_STOREFRONT_API_TOKEN` - Copy the Storefront API access token from \"Apps\" -\u003e \"Develop apps\" -\u003e [your_custom_app] -\u003e \"API credentials\".\n- `SHOPIFY_ADMIN_API_TOKEN` - Copy the Admin API access token from \"Apps\" -\u003e \"Develop apps\" -\u003e [your_custom_app] -\u003e \"API credentials\". (__Note: you’ll only be able to reveal your Admin API token once.__)\n- `SHOPIFY_WEBHOOK_INTEGRITY` - Copy the Integrity hash from \"Settings\" -\u003e \"Notifications\" -\u003e \"Webhooks\" _(very bottom of page)_\n- `KLAVIYO_API_KEY` - Create a Private API Key from your Klaviyo Account \"Settings\" -\u003e \"API Keys\"\n- `MAILCHIMP_API_KEY` - Create an API key from \"Account -\u003e \"Extras\" -\u003e API Keys\n- `MAILCHIMP_SERVER` - This is the server your account is from. It's in the URL when logged in and at the end of your API Key\n- `SENDGRID_API_KEY` - Create an API key from \"Settings\" -\u003e \"API Keys\" with \"Restricted Access\" to only \"Mail Send\"\n\n4. Create an `.env.production` and `.env.development` file in the `/studio` folder, and add the following (using the same value as above):\n\n```\nSANITY_STUDIO_PREVIEW_SECRET=XXXXXX\n```\n\n### 5) Shopify Store Theme\n\nSince we're serving our store through a headless environment, we don't want visitors accessing our unused shopify theme. The domain for this is visible during checkout, and is publicly accessible. To silence it, replace your current theme's `theme.liquid` file with the one from this repo, and replace `YOUR_STOREFRONT_DOMAIN_NO_PROTOCOL` with your actual frontsite domain URL **(do not include protocol or trailing slash)**\n\nThis will essentially \"pass-through\" URLs accessed at your Shopify Store to your true headless storefront _(ie. `shop.hull.dev/products` -\u003e `hull.dev/products`)_\n\n\u003cbr /\u003e\n\n# ⚡ Spin Up\n\n### Next (Front End)\n\n`yarn dev` in the project folder to start the front end locally\n\n- Your front end should be running on [http://localhost:3000](http://localhost:3000)\n\n### Sanity (Back End)\n\n`yarn dev` in the `/studio` folder to start the studio locally\n\n- Your Sanity Studio should be running on [http://localhost:3333](http://localhost:3333)\n  \u003e **Warning** \u003cbr /\u003eIf you did not manually set up your project, the `projectId` in `/studio/sanity.json` will still be set to the HULL demo project. Make sure to update this before starting the studio, otherwise you will be denied access when trying to access your studio.\n\n\u003cbr /\u003e\n\n# 🚀 Deployment\n\n### Vercel\n\nThis is setup to work seamlessly with Vercel, which I highly recommend as your hosting provider of choice. Simply follow the on-screen instructions to setup your new project, and be sure to **add the same `.env.local` variables to your Vercel Project**\n\n### Sanity\n\nThis is an easy one, you can simply run `sanity deploy` from the `/studio` folder in your project. Select a subdomain you want; your Studio is now accessible from the web. This is where I'll invite the client to manage the project so they can both add billing info and begin editing content.\n\n### Client Updates\n\nOnce you hand off to the client you'll want to give them the ability to generate builds when they make updates within the Sanity Studio. The easiest way to do this is through my [Vercel Deploy plugin](https://github.com/ndimatteo/sanity-plugin-vercel-deploy).\n\n\u003cbr /\u003e\n\n# 🤘 Extras/Tips\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eThis looks like a theme... How can I use this like a starter?\u003c/strong\u003e\u003c/summary\u003e\n\nWhile this starter is relatively opinionated, the goal was three-fold:\n\n1. Use high-quality packages that don't get in the way\n2. Solve common UX problems and complex logic so you can focus on the fun stuff\n3. Create a more approachable starter for anyone looking to build production-ready headless Shopify experiences\n\nThat being said, I understand this means a lot of what's included is **very opinionated**. However, you'll find that at it's core the structure and naming conventions lend itself to really making it your own.\n\nI've purposefully used extracted component classes, not only for cleaner file structure, but also so you can easily work in your own styles exclusively within the styles folder. Feel free to extend or outright remove the applied styles for all of the components!\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eWhat's up with the CSS? Why are you using @apply?\u003c/strong\u003e\u003c/summary\u003e\n\nPreviously, `@apply` was used to extract component classes away from your javascript files. However, since then Tailwind has been opposed to this approach. In the coming releases HULL will move away from this approach in favor of applying styles directly to the components so functionality and styling is done in one place.\n\nYou can read more about Tailwind's stance on `@apply` here: https://tailwindcss.com/docs/reusing-styles#avoiding-premature-abstraction\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eCan I use this for non-Shopify projects?\u003c/strong\u003e\u003c/summary\u003e\n\nAbsolutely! This starter was actually born out of a non-shopify starter I had been using for my own client projects.\n\nI made a `marketing-starter` branch that is **HULL without all the Shopify logic**! The fastest way to get started is simply cloning that branch locally into an empty project folder:\n\n```\ngit clone -b marketing-starter --single-branch git@github.com:ndimatteo/HULL.git .\n```\n\nYou can read the [setup instructions](https://github.com/ndimatteo/HULL/tree/marketing-starter#-set-up) for this version from the branch's README.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eError: Failed to communicate with the Sanity API\u003c/strong\u003e\u003c/summary\u003e\n\nIf you get this error in your CLI, you need to logout and log back in again. Simply do `sanity logout` and then `sanity login` to fix.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAccess your \"product_sync\" metafields in Shopify without using a plugin\u003c/strong\u003e\u003c/summary\u003e\n\nSimply navigate directly to: `https://[store_id].myshopify.com/admin/bulk?resource_name=Product\u0026edit=metafields.sanity.product_sync`\n\n_(making sure to replace `[store_id]` with your Shopify Store ID)_\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eHow do I properly hand-off a Vercel project to the client?\u003c/strong\u003e\u003c/summary\u003e\n\nWhile not as easy as Netlify, what I prefer to do is:\n\n1. Have the client create their own [Vercel account](https://vercel.com/signup)\n2. At the time of writing, Github connections can only be connected to one Vercel account at a time, so have the client [create a Github account](https://github.com/join) if they don't already have one, and transfer the project repo to them\n3. Delete the dev project from your own Vercel account (this is so the client can utilize the project name and domain you were using during dev)\n4. You or the client can now connect their newly transferred Github repo to their own Vercel account!\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eHow can I see the bundle size of my website?\u003c/strong\u003e\u003c/summary\u003e\n\nSimply run `yarn analyze` from the project folder. This will run a build of your site and automatically open the [Webpack Bundle Analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) visuals for your site's build files.\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n# 💯 Shoutouts\n\nHuge ups to the following talented and rad folks who helped in countless ways. Thank you for all the support, code contributions, and discussions.\n\n### Developers\n\n- 🔥 [@tuckercs](https://github.com/tuckercs)\n- 🍝 [@iamkevingreen](https://github.com/iamkevingreen)\n- 🧈 [@mikehwagz](https://github.com/mikehwagz)\n- 😎 [@dictions](https://github.com/dictions)\n\n### Designers\n\n- [@thecollectedworks](https://www.instagram.com/thecollectedworks/)\n- [@joyntnotjoint](https://www.instagram.com/joyntnotjoint/)\n\n\u003cbr /\u003e\n\n# 🤝 License\n\n### [MIT](LICENSE)\n\n\u003e [nickdimatteo.com](https://nickdimatteo.com) \u0026nbsp;\u0026middot;\u0026nbsp;\n\u003e Github [@ndimatteo](https://github.com/ndimatteo) \u0026nbsp;\u0026middot;\u0026nbsp;\n\u003e Instagram [@ndimatteo](https://instagram.com/ndimatteo)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndimatteo%2Fhull","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fndimatteo%2Fhull","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndimatteo%2Fhull/lists"}