cimplify
Templates

Customizing a template

The 95/5 split: ~95% of merchant changes are content (`lib/brand.ts`) and palette (`app/globals.css`). For the remaining 5% (restructured layouts, industry-specific sections, custom selectors), eject the SDK component or extend the brand schema.

The customisation tiers

TierWhenCost
Edit brand.tsCopy, links, contact, hero, FAQ, policiesFree updates from the SDK forever
Edit globals.css @themePalette, radius, fontsFree updates from the SDK forever
classNames overridesRestyle a slot inside an SDK componentFree updates; no fork
Eject a componentRestructure JSX, change behaviour, add merchant logicYou own the file; no auto-updates
Extend BrandSchemaIndustry-specific fields (lookbook, booking policy)Type-checked, validated, schema co-evolves

Edit content via brand.ts

Hardcoding a string in a page or component is the wrong move. Hoist it into the brand object first; everything that reads from brand stays consistent across pages, sitemap, and metadata.

// ❌ wrong: hardcoded in a component
<h1>Akua's Bakery</h1>

// ✅ right: sourced from the brand contract
import { brand } from "@/lib/brand";
<h1>{brand.hero.title}</h1>

Eject a component

The SDK ships ~67 ejectable components in its registry (cards, selectors, full pages, primitives). Eject when a classNames override can't reach the part you need to change, or when you're restructuring beyond what the component's props support.

# Browse the registry
cimplify list

# Eject one
cimplify add cart-summary
# → writes ./components/cart-summary.tsx, owned by you

# Replace the SDK import in your page
# import { CartSummary } from "@cimplify/sdk/react"
# import { CartSummary } from "@/components/cart-summary"

Once ejected, the file is yours. SDK upgrades won't touch it; you trade off auto-bugfixes for full control.

Don't reinvent the customizer. Variant selection, add-on math, bundle pricing, and composite mode-switching took many iterations to get right. Eject variant-selector, composite-selector, bundle-selector, or add-on-selector and re-style; don't rewrite the cart payload contract. See AGENTS.md for the full doctrine.

Extend the brand schema

Industry-specific fields belong on the schema, not in scattered component props. Use BrandSchema.extend so your additions get the same boot-time validation as the canonical fields.

lib/schema.ts (services template)
import { BrandSchema } from "@cimplify/sdk/testing";
import { z } from "zod";

export const ServicesBrandSchema = BrandSchema.extend({
  bookingPolicy: z.object({
    cancellationWindowHours: z.number().int().positive(),
    depositPercent: z.number().int().min(0).max(100),
    rescheduleNoticeHours: z.number().int().positive(),
  }),
});

export type ServicesBrand = z.infer<typeof ServicesBrandSchema>;
lib/brand.ts
import type { ServicesBrand } from "./schema";

export const brand: ServicesBrand = {
  // …all the standard Brand fields…
  bookingPolicy: {
    cancellationWindowHours: 24,
    depositPercent: 25,
    rescheduleNoticeHours: 12,
  },
};
__tests__/brand.test.ts: assert against the extended schema
import { describe, it, expect } from "vitest";
import { ServicesBrandSchema } from "../lib/schema";
import { brand } from "../lib/brand";

describe("brand", () => {
  it("conforms to the services brand contract", () => {
    expect(ServicesBrandSchema.safeParse(brand).success).toBe(true);
  });
});

Add a new section

  1. Build the section as a Server Component in components/.
  2. Read merchant-specific copy from brand; extend the schema if the field isn't there.
  3. Wrap any client interactivity in a *-client.tsx island behind <Suspense> to keep the chrome cached.
  4. Compose into the page (app/page.tsx, app/products/[slug]/page.tsx, …).
  5. Run bun run check.

Wire a Server Action

"use server";
import { getServerClient, revalidateOrders } from "@cimplify/sdk/server";

export async function cancelMyOrder(orderId: string) {
  const r = await getServerClient().orders.cancel(orderId, "customer requested");
  if (!r.ok) return { ok: false as const, message: r.error.message };

  await revalidateOrders();
  return { ok: true as const };
}

Don'ts

  • Hardcode strings in pages or components.
  • Disable cacheComponents: true to silence a warning. Wrap in <Suspense> instead.
  • Use unstable_cache. Next 16's canonical primitive is 'use cache'.
  • Bypass getServerClient() by instantiating createCimplifyClient directly in a Server Component; you'll lose per-request memoisation.

Next

  • Brand schema Field reference and a full example

  • Testing Catch ejections that broke the contract

On this page