Command Palette

Search for a command to run...

Theme SDK › Theme SDK Reference

Theme SDK Reference

Author canonical theme sections on the shared surface foundation.

Theme SDK Reference

Use @commercit/theme-sdk for theme-owned, workspace-editable sections.

Theme sections and app sections now share:

  • one UI foundation
  • one section runtime
  • one binding model
  • one Storefront Section Data Contract

They differ only in ownership and lifecycle:

  • theme sections are editable in the workspace
  • app sections are installable and read-only at the code level

Imports

import {
  defineSection,
  formatPrice,
  t,
  useBoundAction,
  useBoundData,
  useLocale,
  useMarket,
  useThemeTokens,
} from '@commercit/theme-sdk'
import {
  Badge,
  Button,
  Card,
  CardContent,
  CardHeader,
  CardTitle,
  Container,
  SectionShell,
  Stack,
} from '@commercit/theme-sdk/ui'

defineSection

Each section also has a sibling section.meta.ts file. The meta file must export sectionContract; this is the public data contract used by published storefronts, preview, AI, and app sections. Sections must not read raw pageContext or undeclared product/shop/page fields.

import {
  SECTION_CONTRACT_VERSION,
  defineStorefrontDataBlocks,
  defineStorefrontSectionManifest,
  type StorefrontSectionProps,
} from '@commercit/theme-sdk'

export const sectionContract = defineStorefrontSectionManifest({
  contractVersion: SECTION_CONTRACT_VERSION,
  slug: 'shared-header',
  region: 'header',
  renderMode: 'interactive',
  capabilities: ['cart.open', 'search.open', 'search.query', 'ui.state'],
  data: defineStorefrontDataBlocks([
    { type: 'shopIdentity', fields: ['name', 'logo'] },
    { type: 'navigation', menu: 'main', fields: ['label', 'url', 'children'] },
    { type: 'languageSwitcher' },
    { type: 'cartShell' },
  ] as const),
})

export type SharedHeaderProps = StorefrontSectionProps<typeof sectionContract>
import { defineMessages, defineSection, formatPrice, t } from '@commercit/theme-sdk'
import { Button, Card, CardContent, CardHeader, CardTitle } from '@commercit/theme-sdk/ui'

const copy = defineMessages('editorial', {
  heading: 'Editorial feature',
  cta: 'Explore',
  body: 'Orders over {threshold} qualify for premium delivery messaging.',
})

export default defineSection({
  messageCatalog: Object.values(copy),
  component: function EditorialPromo({ config }) {
    return (
      <Card>
        <CardHeader>
          <CardTitle>{String(config.heading ?? t(copy.heading))}</CardTitle>
        </CardHeader>
        <CardContent>
          <p className="text-sm text-muted-foreground">
            {t(copy.body, { threshold: formatPrice(50) })}
          </p>
          <Button>{String(config.cta_label ?? t(copy.cta))}</Button>
        </CardContent>
      </Card>
    )
  },
})

New theme sections should emit extracted messageCatalog entries via defineMessages(...) and optional msg(...). Packaged theme copy stays language-based in code; market-specific overrides belong to the storefront localization engine.

Shared UI primitives

Use @commercit/theme-sdk/ui instead of raw internal imports for new theme sections.

Public primitives include:

  • Button, Badge
  • Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter
  • Input, Textarea, Label, Select, Checkbox, RadioGroup, Switch, Slider
  • Accordion, Tabs, DropdownMenu
  • Dialog, Drawer, Sheet, Popover, Tooltip
  • Skeleton, Progress, Carousel
  • SectionShell, Container, Stack, Inline, Grid

useThemeTokens

import { useThemeTokens } from '@commercit/theme-sdk'

export default function Example() {
  const tokens = useThemeTokens()

  return (
    <div
      style={{
        color: tokens.colors.text,
        background: tokens.colors.surface,
        borderColor: tokens.colors.border,
      }}
    >
      Shared theme-aware UI
    </div>
  )
}

Storefront Localization Helpers

Theme sections receive host-resolved storefront localization context.

Available helpers:

  • useTranslations()
  • t(descriptor, params?)
  • useLocale()
  • useMarket()
  • useCurrency()
  • useDirection()
  • formatPrice(amount, options?)
  • formatDate(value, options?)
  • formatNumber(value, options?)

Example:

import { defineMessages, defineSection, formatPrice, t, useLocale, useMarket } from '@commercit/theme-sdk'
import { Card, CardContent, CardHeader, CardTitle } from '@commercit/theme-sdk/ui'

const copy = defineMessages('promo', {
  heading: 'Free shipping',
  body: 'Orders over {threshold} ship free.',
})

export default defineSection({
  messageCatalog: Object.values(copy),
  component: function PromoStrip() {
    const locale = useLocale()
    const market = useMarket()

    return (
      <Card>
        <CardHeader>
          <CardTitle>{t(copy.heading)}</CardTitle>
        </CardHeader>
        <CardContent>
          <p>{t(copy.body, { threshold: formatPrice(50) })}</p>
          <p className="text-sm text-muted-foreground">
            locale: {locale} · market: {market.marketHandle ?? 'default'}
          </p>
        </CardContent>
      </Card>
    )
  },
})

See Storefront Localization Engine for market assignment, fallback rules, and merchant overrides.

useBoundData and useBoundAction

Theme sections can consume both core and app-owned runtime surfaces through host bindings.

import { defineSection, useBoundAction, useBoundData } from '@commercit/theme-sdk'
import { Button, Card, CardContent, CardHeader, CardTitle, Stack } from '@commercit/theme-sdk/ui'

type ReviewSummary = {
  average_rating?: number
  review_count?: number
}

export default defineSection({
  component: function ProductReviewSummary({ config }) {
    const summary = useBoundData<ReviewSummary>('summary') ?? {}
    const openReviews = useBoundAction('open_reviews')

    return (
      <Card>
        <CardHeader>
          <CardTitle>{String(config.heading ?? 'What shoppers think')}</CardTitle>
        </CardHeader>
        <CardContent>
          <Stack gap="sm">
            <p className="text-3xl font-semibold">
              {typeof summary.average_rating === 'number' ? summary.average_rating.toFixed(1) : '—'}★
            </p>
            <p className="text-sm text-muted-foreground">
              {typeof summary.review_count === 'number' ? summary.review_count : 0} verified reviews
            </p>
            <div>
              <Button onClick={() => void openReviews({ path: '/pages/reviews' })}>
                Read all reviews
              </Button>
            </div>
          </Stack>
        </CardContent>
      </Card>
    )
  },
})

Runtime rules:

  • bind by unified surface moduleId
  • never bind by slug
  • data is read through useBoundData(alias)
  • actions are invoked through useBoundAction(alias)

AI-generated custom sections

When AI creates a workspace section that depends on app outputs:

  1. inspect({ what: "surfaces" }) finds the exact moduleId
  2. the generated section imports from @commercit/theme-sdk / @commercit/theme-sdk/ui
  3. the write call includes a bindings payload
  4. the host stores the binding contract in settings._bindings

This keeps the section runtime unified without changing canonical section persistence.

What does not belong in the theme SDK

Theme SDK must not expose app-only concerns:

  • app/admin capabilities
  • app storage
  • secrets
  • jobs
  • events
  • App API access

For those, use @commercit/app-sdk.