Tanqory themes
Build storefront themes in React
A Tanqory theme is a React and TypeScript app. Sections are components, pages are JSON the merchant edits in the visual editor, and store data arrives through typed hooks. Published themes are delivered from Cloudflare's edge.
sofia@pampa ~ % npm install -g @tanqory/clisofia@pampa ~ % tanqory loginOpening your browser to authorize…✓ Logged in as sofia@example.com (via https://api.tanqory.com/api)sofia@pampa ~ % tanqory theme init pampa --name "Pampa"Downloading the Nova starter from github.com/tanqory/tanqory-theme-nova…Creating the theme in your store…✓ Theme "Pampa" scaffolded + created in your store (see the Theme library).sofia@pampa ~ % cd pampa && pnpm installsofia@pampa ~ % tanqory theme dev✓ Live data from pampa-mate → http://localhost:4321sofia@pampa ~/pampa % Theme anatomy
How a theme is put together
One rule runs through the whole folder: .ts and .tsx files are code you write, .json files are content the merchant's editor writes. Your code and the merchant's words live in separate files, so a code update leaves their pages as they wrote them.
- SectionsReact components in sections/, each registered with defineSection. The Nova starter ships 62, from the hero and product grid to the store locator.
- BlocksSmaller pieces, such as a slide or an Add to cart button, that exist only inside the sections that list them as allowed blocks.
- TemplatesOne JSON file per page type, from index and product to cart and 404, listing its sections in order. A suffix such as product.bundle adds an alternate template.
- Header and footer groupsgroups/header.json and groups/footer.json are written once and shared by every template that binds them.
- Theme settingsconfig/settings.schema.ts declares the theme's colours, type and spacing; config/settings.json keeps what the merchant picked. Style settings update live in the editor.
- LocalesThe theme's own interface strings per language in locales/. The starter ships English and Thai.
pampa/├── sections/ React sections and blocks│ ├── Hero.tsx│ ├── ProductGrid.tsx│ └── AddToCart.tsx├── components/ shared parts, not placeable├── templates/ one JSON page per page type│ ├── index.json│ ├── product.json│ └── product.bundle.json├── groups/ header.json, footer.json├── config/ settings.schema.ts, settings.json├── layouts/ layout.tsx├── locales/ en.json, th.json├── assets/├── entry.ts render entry└── tanqory.json the linked store and themeSections and settings
Sections the editor understands
One defineSection call tells the editor everything it needs: the name it lists, the settings it draws and the values a new copy starts with. You never build a settings form.
- defineSectionName, title, category and role (section or block). requiresContext keeps a product block off the cart page.
- Setting types20 types, from text, rich text, colour and range to image, video, product, collection, blog and menu pickers, with defaults, options and visible_if.
- PresetsReady arrangements of a section and its blocks that the merchant adds in one step.
- theme.manifest.jsonGenerated from your sections on every build and read by the editor's section picker, so what you ship is exactly what a merchant can add.
import { defineSection, type SectionProps } from '../lib/tanqory'export function Hero({ attributes }: SectionProps) { const a = attributes as Record<string, string | undefined> return ( <section className="hero-section" data-size={a.size}> <h1>{a.heading}</h1> <a href={a.buttonLink}>{a.buttonLabel}</a> </section> )}export default defineSection({ name: 'hero', role: 'section', title: 'Hero', category: 'layout', attributes: { heading: { type: 'text', label: 'Heading', default: 'Mate, the slow way' }, buttonLabel: { type: 'text', label: 'Primary button', default: 'Shop the gourds' }, buttonLink: { type: 'url', label: 'Primary button link', default: '/collections/gourds' }, size: { type: 'select', label: 'Height', default: 'large', options: [ { value: 'medium', label: 'Medium' }, { value: 'large', label: 'Large' }, ], }, }, component: Hero,})- Heading
- Mate, the slow way
- Primary button
- Shop the gourds
- Primary button link
- /collections/gourds
- Height
- Large
Store data
Store data through typed hooks
There is no template language to learn. A section calls a hook and gets typed products, collections, the cart and the theme's settings, with your editor's autocomplete on every field.
- useDataProducts, collections, pages, menus, search, recommendations, blogs and metaobjects, plus a raw storefront GraphQL query for anything else.
- useCartAdd, change quantities, remove and apply discount codes; every part of the theme that shows the cart follows.
- useSettings and useTThe theme settings the merchant chose, and interface strings in the shopper's language.
- useBoundDynamic sources: the merchant binds a setting to a product field or metafield instead of typing it.
- HelpersformatMoney, formatDate and imageUrl are plain functions where older theme languages use filters.
import { useCart, useData, formatMoney } from '../lib/tanqory'export function FeaturedGourd() { const { productByHandle } = useData() const { add } = useCart() const gourd = productByHandle('calabash-gourd') if (!gourd?.variantId) return null return ( <article> <h2>{gourd.title}</h2> <p>{formatMoney(gourd.price)}</p> <button onClick={() => add({ variantId: gourd.variantId!, quantity: 1 })}> Add to cart </button> </article> )}Tanqory CLI
Work on live store data from your laptop
@tanqory/cli scaffolds the starter, runs it on a development store's real products and sends your code back. Link a folder once in tanqory.json and later commands need no flags.
- tanqory theme initDownloads the Nova starter and creates the theme in the development store you pick.
- tanqory theme devVite on localhost:4321 with the store's live data, or --mock to work offline.
- --syncA second after you save, the edit reaches the store's copy of the theme and its preview and visual editor rebuild.
- tanqory theme pull and pushPull a theme's source into a folder; push your code back and the storefront rebuilds.
- tanqory loginSigns you in through the browser, or takes a personal access token. In CI, set TANQORY_TOKEN.
sofia@pampa ~/pampa % tanqory theme dev --sync✓ Live data from pampa-mate → http://localhost:4321✓ Live-sync ON → store theme 6702f3c1a9b4 (edits push automatically) Editor: https://app.tanqory.com/admin/store/66f0b2e4d17a/themes/6702f3c1a9b4/builder✓ synced → store theme (preview + editor rebuilding)sofia@pampa ~/pampa % tanqory theme pushPackaging + uploading theme source…✓ Pushed to 6702f3c1a9b4. The storefront is rebuilding: see it in the Theme library.sofia@pampa ~/pampa % Check and sell
Check it, submit it, list it
The starter carries its own quality gates. Run them, submit from the CLI, and the theme goes through automated checks and a person's review before merchants can install it from the Theme Store.
- pnpm verifyType check, manifest and spec checks, a check that every setting is read by its section, the design rules, page headings, tests, the build and product structured data, in one command.
- pnpm verify:liveAccessibility checks with axe over 10 routes and touch targets at 4 screen widths, against the running theme.
- tanqory theme submitSends the theme with its name, tagline, category, thumbnail and demo store. A ZIP of up to 20 MB can go through the developer dashboard instead.
- Theme StoreApproved themes are listed in the Theme Store, where every Tanqory merchant can install them.
sofia@pampa ~/pampa % pnpm verify && tanqory theme submit --name "Pampa" --tagline "Calm, photo-led theme for small food brands" --demo-url https://pampa-mate.mytanqory.com✓ Submitted "Pampa" for review (status SUBMITTED).sofia@pampa ~/pampa % - Submitted
- Automated checks
- Human review
- Approved
- Live in the Theme Store
Start your first theme
Open a store, install the CLI and run the Nova starter on your own products. What you build stays editable for the merchant in the visual editor.
- React 18, TypeScript and Vite
- Live store data on localhost
- Submit from the CLI