Theme overrides without changing another store's behavior
Keep theme-specific rendering and behavior behind explicit boundaries so shared packages do not silently alter unrelated stores.
In this guide
A local improvement becomes a global regression
One store needs a purchasing dashboard instead of the standard hero. A developer changes a shared component and verifies the requested store. Another store now receives the same empty dashboard because it has no supporting account configuration. The change was scoped visually in the developer's mind, but not in the code.
Treat a theme override as a boundary for behavior as well as CSS. Shared components can affect navigation, defaults, data loading, validation, and saved content. A style namespace alone cannot prevent a theme-specific data request or fallback rule from running in another store.
Identify what the theme actually owns
List the intended differences: typography, layout, widget presentation, optional capabilities, or content defaults. Keep business rules such as authorization and price calculation outside purely visual selection. A theme may present a purchasing capability, but should not grant it.
Locate the target SDK's supported registration seam and identify the base behavior to preserve. Prefer composition around a narrow renderer over copying a large vendor component into the custom theme. Copies make upgrades harder because later fixes in the original no longer reach the duplicated implementation automatically.
Select a theme-owned view model
select-hero-model.ts implements three explicit states: configured content, a theme-specific starter, and the base empty state. It preserves an intentionally cleared heading rather than replacing it with a default. The included assertions capture the two boundaries most likely to regress.
Call the selector from your theme's registered renderer, using capabilities derived from trusted theme configuration. Znode's documented override-component-list maps saved component keys to configurations. Keep this presentation selection separate from account authorization and retain existing component keys when changing appearance.
theme/select-hero-model.ts typescript
export type SavedHero = Readonly<{
heading?: string | null; actionLabel?: string | null; actionPath?: string | null;
}>;
export type HeroModel = Readonly<{
kind: "configured" | "starter" | "empty";
heading: string; actionLabel: string; actionPath: string;
}>;
export type TrustedTheme = Readonly<{ purchasingStarter: boolean }>;
export function selectHero(saved: SavedHero, theme: TrustedTheme): HeroModel {
if (saved.heading !== undefined && saved.heading !== null) {
return {
kind: "configured", heading: saved.heading,
actionLabel: saved.actionLabel ?? "", actionPath: saved.actionPath ?? ""
};
}
if (theme.purchasingStarter) {
return {
kind: "starter", heading: "Your purchasing workspace",
actionLabel: "View resources", actionPath: "/resources"
};
}
return { kind: "empty", heading: "", actionLabel: "", actionPath: "" };
}
const cleared = selectHero({ heading: "" }, { purchasingStarter: true });
if (cleared.kind !== "configured" || cleared.heading !== "")
throw new Error("An intentionally cleared heading must remain cleared.");
const base = selectHero({}, { purchasingStarter: false });
if (base.kind !== "empty") throw new Error("Base theme must retain its empty state.");
// Map trusted theme selection in the installed theme configuration.
// Render local links through the same URL policy used by ResourcePanel.
Preserve the saved contract
A theme can change how a widget looks without changing the identity of its saved component. When extending fields, preserve supported existing properties and define how old configurations behave. Defaults should fill missing content according to policy, not overwrite intentional empty values on every render.
Test the editor and storefront together. A custom registration in one application can make preview look correct while the other still uses a different renderer. Record both application artifacts and the selected store theme with release evidence. Shared source files do not guarantee the deployed applications contain the same code.
Compare the base and custom stores
Use two fictional stores: one on the base theme and one on the custom theme. Render the same saved widget in both, then test the custom empty state. Verify that the base store retains its existing behavior and does not request theme-specific data.
Include old saved configurations, configured standard content, missing optional fields, and invalid links. Check narrow layouts and keyboard interaction in both stores. If a shared component was changed, run the relevant base behavior checks as well as the new custom case. Do not equate a successful custom screenshot with absence of regressions elsewhere.
- Scope styles under a deliberate theme root.
- Keep capability checks separate from user permissions.
- Preserve saved component keys unless a migration is explicit.
- Avoid duplicating vendor code without a maintenance reason.
Leave an upgrade trail
Document each override's purpose, original seam, and expected fallback. Identify any vendor behavior intentionally replaced and the checks that prove it remains safe. During an SDK upgrade, review those boundaries against the restored target packages rather than assuming matching filenames mean matching contracts.
A well-contained theme gives designers room to create a distinctive store while keeping platform behavior understandable. The important question is not only whether the custom store looks right. It is whether the customization's reach is explicit enough that another store can continue operating without inheriting decisions it never requested.
References and further reading
Independent guidance from GCG. Znode is a trademark of its owner. Examples use fictional data and are not official platform documentation. Suggest a correction.