Customisation
The template is a complete SaaS website. Most buyers want most of it. The few that don't can delete what they don't use — every opt-out has a documented deletion path.
Where buyers customise (in order of "first touch")
src/template.config.ts— name, URL, contact email, locales, theme defaults, feature flags. Nothing else in the codebase reads brand values from anywhere else..env.local— Supabase, Stripe, Resend keys.src/locales/en.ts(and the 9 others) — copy. Use/i18n-syncto keep locale files aligned.app/(public)/page.tsx— section ordering on the landing.src/screens/landing/*.tsx— visual changes per landing section.src/index.css— base theme tokens (CSS variables).src/lib/plans.ts— pricing tiers.src/utils/color-presets.ts— extend or replace presets.
Adding a new locale
Three steps:
// 1. src/locales/de.ts
const deLocales = {
nav: { home: 'Startseite' /* ... */ },
};
export default deLocales;
// 2. src/template.config.ts → LANGUAGES (add an entry)
{ value: 'de', label: 'Deutsch', icon: '/assets/icons/flags/ic_flag_de.svg' },
// 3. src/locales/i18n.ts → register
import de from './de';
i18n.addResourceBundle('de', 'translation', de);Drop a flag SVG at public/assets/icons/flags/ic_flag_de.svg. The
header switcher and sitemap pick it up automatically.
Adding your own resource (CRUD)
The Posts module is the worked example. Pattern:
- Add the model to
prisma/schema.prisma. - Write a paired raw SQL migration that enables RLS + explicit
policies. Add a row to
RLS_EXPECTATIONSinsrc/lib/rls.ts. - Define Zod schemas in
src/lib/<resource>.ts. Keep this module free ofprisma/server-only imports so the form (a client component) can re-use the schema. - Build the route handlers under
app/api/<resource>/route.ts(public) andapp/api/admin/<resource>/route.ts(admin write). - Build the screens under
src/screens/<area>/. - Wire pages, paths, and the admin sidebar entry.
- Write at least one e2e spec covering the public read + admin write.
bun run verify:rls will fail if the policies on the new table drift
from RLS_EXPECTATIONS.
Deletion paths (opt-out features)
Remove the blog
# In src/template.config.ts:
FEATURES.blog = falseThe footer link disappears, the /blog route stops building (it
reads the flag), the admin Posts editor is hidden. Optional cleanup —
delete prisma/migrations/0005_posts.sql and app/(public)/blog/.
Remove the admin panel entirely
Delete:
app/admin/(the whole route group)src/screens/admin/src/components/admin/app/api/admin/
The RoleBasedGuard and requireAdmin helpers stay (they are pure
helpers, no cost) but no route imports them after the deletion.
Remove auth + billing entirely
This is a non-trivial fork — see the Kettle & Co customer story for a real-world example. Documented path:
- Delete
app/(auth)/,app/(app)/,app/admin/. - Delete
app/api/auth/,app/api/me/,app/api/admin/,app/api/stripe/. - Delete
src/contexts/jwt-session-context.tsxand the auth hooks. - Update
src/screens/landing/landing-header.tsxto drop the auth CTA.
The public marketing surface stays intact.
Theme tokens
The template uses CSS variables for the entire theme. They live
under the :root block in src/index.css. The Theme Studio at
/settings/theme writes the user's picks back to localStorage,
which is read by src/contexts/settings-context.tsx on boot.
To change the brand defaults (what visitors see before they touch the Theme Studio):
// src/template.config.ts
export const defaultSettings: SettingsValueProps = {
themeMode: 'system',
themeColorPresets: 'blue', // ← change me
themeFontSize: 16,
/* ... */
};If you want to drop the Theme Studio entirely, set
FEATURES.themeStudio = false. The header switcher disappears and
the /settings/theme route stops building.