Customization
Rebrand the boilerplate by editing the identity, logo, social, and SEO fields in @repo/config plus your English i18n copy - one source of truth per surface.
Branding lives in the config object exported from packages/config/src/index.ts (@repo/config); all user-facing copy lives in i18n translation files. Rebranding is editing a handful of fields, each with a single source of truth.
Brandable surfaces
| Surface | Where | Ships as |
|---|---|---|
| siteName | config.siteName | Your project name from init |
| fullSiteName | config.fullSiteName | Your project name from init |
| domain | config.domain (bare, no protocol) | Derived from your init base URL |
| baseUrl | config.baseUrl | Your init base URL, overridden server-side by BASE_URL |
| logo | config.logo | { main, square } SVG paths |
| business | config.business | { name, address, registrationNumber } |
| phone | config.phone | { number, formatted } |
| social | config.social | Empty - add the profiles you have |
| seo | config.seo | { description, foundingDate, productRating } - productRating off unless set |
| landing | socialProofAvatars in packages/config/src/landing.ts | 5 placeholder webp faces |
| footer | getFooterColumns() in packages/config/src/footer.ts | i18n-keyed columns |
| All copy | i18n translation files | en/*.json |
Identity fields
Edit these top-level keys in packages/config/src/index.ts:
// @repo/config - pure static data, no process.env
export const config = {
siteName: "Acme", // short name in titles + UI
fullSiteName: "Acme.com",
domain: "acme.com", // bare domain, no protocol
baseUrl: "https://acme.com", // BASE_URL overrides this on the server
indexable: true, // false on demo/staging to block SEO + sitemap
logo: { main: "/images/logo.svg", square: "/images/logo-square.svg" },
business: { name: "Acme Inc.", address: "...", registrationNumber: "..." },
phone: { number: "+1234567890", formatted: "+1 (234) 567 890" },
social: { x: "https://x.com/acme", youtube: "..." },
seo: { description: "...", foundingDate: "2024-01-01" },
// ...feature flags
};- logo and avatars: config holds the paths; replace the asset files in the app's public directory. Optional
logowidth/heightfall back to 184x184 for structured data. - social: add
discord,linkedin,instagram,facebook,tiktok,pinterest,github, ortelegram; absent links are hidden in the footer. - footer: columns are i18n-keyed (
footer.columns.*); content items carry asectionand vanish when that section has no content. Legal and contact links are static.
Use config.routes, never hardcoded paths
Reference config.routes.* for navigation and redirects so one change updates every link. Defaults: home: "/", auth: "/auth", loginRedirect: "/dashboard", dashboard: "/dashboard", pricing: "/#pricing", onboarding: "/onboarding", settings: "/settings/profile", notifications: "/notifications", admin: "/admin/users". Full list in configuration.
Stay update-friendly
You own this code - build anything you want. Source identity from config and i18n instead of hardcoding, and generatesaas update stays clean:
- Brand name:
config.siteName, never a literal. - Copy: generic i18n keys (
dashboard.welcome_message, notdashboard.welcome_to_acme).
Config values stay yours and restaged shared files merge cleanly. Rewrite shared files freely - you resolve those diffs on update, see the update guide.
Replace the placeholder content
Sample content ships so the app looks alive. Swap it for yours:
- Dashboard badges: the
Demo/Placeholdermarks (demo.*andplaceholder_*keys) on the usage and recent-activity cards. Wire real data, drop the badges. - Landing demo video: points at a placeholder clip. Replace it or remove the section.
- Social-proof faces:
socialProofAvatarsinpackages/config/src/landing.ts.
Hide a feature by toggling its flag, not by deleting UI - config.payment, config.notifications and the rest already hide themselves when enabled is false. And edit only en/*.json under packages/i18n/translations/: other locales are generated from English and direct edits get overwritten. See Internationalization.