Build with DesignMint.
Everything you need to put a product configurator on your storefront: install the SDKs, author design rules in the Back Office, and mount the Studio your customers use. This is the usage guide — for a live feel of the end result, open the sandbox.
Two SDKs, one contract.
DesignMint ships as two React packages that share a single data contract. The Back Office (@intuio/designmint-back-office-sdk) is the merchant workspace where you author design rules — surfaces, print areas, physical sizes, variants, pricing. Saving it produces one serialized metadata string. The Studio (@intuio/designmint-studio) is the customer-facing canvas that consumes that metadata and turns a shopper's design into validated, print-ready output.
Your application sits between them: it persists the metadata after Back Office saves, passes it to the Studio at render time, and stores the customer's canvas data so designs can be restored later.
Install and get access.
Both packages are published privately under the @intuio scope on npm. You need an access token from the DesignMint team — request one at designmint@intuio.io. Add it to the .npmrc in your project root (keep it out of version control), then install:
1# .npmrc2@intuio:registry=https://registry.npmjs.org/3//registry.npmjs.org/:_authToken=${INTUIO_NPM_TOKEN}45# install6npm install @intuio/designmint-studio7npm install @intuio/designmint-back-office-sdkTailwind setup. Add the package to your Tailwind content globs so its utility classes are generated, and use the class-based dark-mode strategy — the Studio follows a .dark class on your document root, not the OS preference:
1module.exports = {2 darkMode: 'class',3 content: [4 './src/**/*.{js,jsx,ts,tsx}',5 './node_modules/@intuio/designmint-studio/dist/**/*.js',6 ],7}Mount it in one component.
The Studio is the default export and needs no providers or wrappers. Give it the metadata your Back Office produced, your priced catalog, and your backend credentials — it handles the rest:
1import DesignMintStudio from "@intuio/designmint-studio"23export function ProductStudio({ metadata, products, customer }) {4 return (5 <div className="h-screen min-h-0">6 <DesignMintStudio7 metadata={metadata} // Base64 string from the Back Office save()8 products={products} // your priced catalog (see Product catalog)9 productCanvasData={{}} // or a previously saved canvas-data map10 backendUrl={BACKEND_URL}11 publishableKey={PUBLISHABLE_API_KEY}12 customerId={customer?.id ?? null}13 currencyCode="usd"14 onAddToCart={async (items) => { /* create the cart lines */ }}15 />16 </div>17 )18}Props reference.
Required props first. Money and currency codes are always lowercase (e.g. "usd").
The product catalog contract.
products carries your priced catalog. Three rules matter most: every product.id must match a productId inside the metadata; money is always a per-currency record like { usd: 45 }; and color swatches appear only when every variant declares attributes.colorCode.
1{2 id: "prod_…", // must match a productId in the metadata3 title: "Windbreaker Jacket",4 basePrice: { usd: 45 },5 variants: [{6 id: "variant_…",7 title: "Red / S",8 attributes: { color: "Red", colorCode: "#DC2626", size: "S" },9 price: { usd: 40 },10 inventoryQuantity: 3, // enables the stock guard at ordering11 }],1213 // Quantity pricing — provide ONE of the two:14 quantityTiers: [{ minQty: 12, pricePerUnit: { usd: 40 } }], // stepper15 fixedQuantities: [{ quantity: 500, totalPrice: { usd: 17500 } }], // dropdown1617 availablePrintingTechniques: [{18 id: "sublimation", label: "Sublimation",19 perDesignPrice: { usd: 12 }, designNames: ["Design 1", "Back"],20 }],21}Unit prices resolve in a fixed order: fixed-quantity match → quantity tier → variant price → product basePrice. A tier without maxQty means "and up".
Saving, exports and add to cart.
Persistence is imperative. Pass a studioRef and call its methods when you want output — the Studio never saves anything on its own:
1const studioRef = useRef(null)23// Full export — file CONTENT (data URLs / bytes), not hosted URLs:4const { exports, productCanvasData } = await studioRef.current.save()56// Per-variant white-background mockup JPEGs:7const { variants } = await studioRef.current.legacySave()Persist productCanvasData and pass it back on the next mount to restore the canvas exactly, decorations included. To upload the returned file content, use the bundled helpers — uploadStudioExports, uploadStudioMockups and saveBrandAssetsToDb. The upload helpers run all uploads in parallel and reject the whole call if any single upload fails, so wrap them in try/catch. Their optional authToken falls back to the stored Medusa admin token.
When a customer confirms an order, the Studio exports and uploads the files, then calls onAddToCart with one item per line: { variantId, quantity, metadata: { decorationId, files, thumbnail, personalization } }.
The merchant workspace.
The Back Office takes exactly five props and talks to no backend of its own — saving is host-driven through an imperative handle, and the SDK never persists anything itself:
1import { useRef } from "react"2import { DesignMintBackOffice } from "@intuio/designmint-back-office-sdk"34const backOfficeRef = useRef(null)56<div className="flex h-full min-h-0">7 <DesignMintBackOffice8 actor="admin" // admin | vendor | microstore9 backOfficeRef={backOfficeRef}10 metadata={savedMetadata} // "" for a fresh session11 products={products}12 editableVariants={true}13 />14</div>1516// Save — validate, then persist the returned string:17const result = await backOfficeRef.current?.save()18if (result?.success) await api.saveMetadata(result.metadata)19else showErrors(result.errors) // [{ message }]Validation gates saving: every design area must resolve to a print size and every required component must be linked, or save() returns success: false with a clear error list and the canvas marks the offending areas. For product cloning, the SDK exports remapMetadataIds and remapCanvasDataIds to rewrite database ids inside saved payloads, and MetadataUtils if you need to inspect metadata server-side.
Authoring a product, step by step.
Start a document
File → New opens an Illustrator-style dialog: pick a real print format from the paper catalog (A-series, letter, posters, cards, brochures…) or set custom width, height and orientation; choose how many design surfaces you need, the export DPI (150 / 300 / 600), CMYK or RGB, per-side safe margins, and at least one output format (SVG, PDF, PNG).
Add design surfaces
Use the Assets panel to upload product photos (JPG, PNG, WebP up to 10 MB) or paste an image URL. Clicking an image applies it to the active design view and variant. Calibrated paper sheets from step 1 need no upload — they are generated locally.
Draw the print areas
Press D and drag on the canvas. Areas snap to edges, centers and guides (hold Alt to disable). Move, resize with the eight handles, restack from the Layers panel, and use the right-click menu for copy, paste, duplicate, align and fit/fill.
Set true-to-size printing
On a calibrated sheet the scale is fixed by the paper size. On a photo mockup, type an area's real print size (in / cm / mm) once to declare the scale — one scale per design keeps everything consistent, and the panel shows the resulting pixel output and DPI live.
Cover the variants
The right rail switches products and variants; variants with color codes group into swatches so you author once per color. Fill per-variant images by upload, URL, recolor (perceptual tint with intensity and brightness), a bulk ZIP upload, or Replicate — which copies areas and images across products and variants, matched by name or position.
Configure the customer experience
The gear opens design configuration: sell the product with the full Studio, a simple form, or as-is. In Studio mode, seven toggles decide what customers may use — text, icons, uploads, colors, layers, embroidery, templates. Reusable input components (text, image, email, number, URL, embroidery) attach to specific areas from the Components tab.
Price it
Set base price, per-size surcharges and technique pricing (flat per-design, or stitch-count tiers for embroidery). The price bar shows base · decoration · total as you work, with a full per-size breakdown one click away.
Save
Hit ⌘ S or your Save button — the host calls save(), validation runs, and on success you receive the metadata string to persist. Amber markers point at anything that blocks saving.
Shortcuts worth learning: D design tool · P pan · Tab cycle areas · ⌘ Z/⌘ ⇧ Z undo/redo · ⌘ scroll zoom at cursor · Esc deselect.
What your customers get to do.
Mounted without isAdmin, the Studio opens in the ordering view with an Edit Design expander. Customers work with exactly the tools you enabled per product: template library, styled text (with curves and effects), icons and shapes, their own uploads (JPG, PNG, GIF, SVG, DST stitch files, even custom fonts), fills and per-element color control, layers, and embroidery with live stitch counts.
The canvas keeps them honest without blocking them: live print-size badges, safety and bleed pills, and per-element warnings when artwork resolution drops below the target DPI or drifts outside the printable area. Switching a color variant swaps the product photo — never their artwork. Up to 20 saved design variations per product let them explore before committing.
Ordering is a line-item table: option dropdowns and color swatches per row, quantity as a dropdown (fixed lots) or a tier-snapping stepper, a per-row Personalize drawer for the input components you defined, and a stock guard that blocks over-stock rows. Add To Cart exports the print files, uploads them, and hands your onAddToCart callback one clean item per line.
Metadata is the contract.
Everything the two SDKs agree on travels in one Base64-encoded JSON string, currently format v3.0. The Back Office produces it, your application stores it, the Studio consumes it. Treat it as opaque: never parse or hand-edit it — both SDKs decode it internally, and MetadataUtils exists for the rare server-side inspection.
Round-trips are guaranteed: feeding a saved string back restores identical state. Products or variants that no longer exist in your catalog are silently filtered out on load. When you clone a product, rewrite the ids inside both payloads with remapMetadataIds / remapCanvasDataIds instead of regenerating the design work.
When something looks off.
Still stuck? Write to designmint@intuio.io — the engineers who built DesignMint are the ones on the pager.