GenerateSaaS

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

SurfaceWhereShips as
siteNameconfig.siteNameYour project name from init
fullSiteNameconfig.fullSiteNameYour project name from init
domainconfig.domain (bare, no protocol)Derived from your init base URL
baseUrlconfig.baseUrlYour init base URL, overridden server-side by BASE_URL
logoconfig.logo{ main, square } SVG paths
businessconfig.business{ name, address, registrationNumber }
phoneconfig.phone{ number, formatted }
socialconfig.socialEmpty - add the profiles you have
seoconfig.seo{ description, foundingDate, productRating } - productRating off unless set
landingsocialProofAvatars in packages/config/src/landing.ts5 placeholder webp faces
footergetFooterColumns() in packages/config/src/footer.tsi18n-keyed columns
All copyi18n translation filesen/*.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 logo width/height fall back to 184x184 for structured data.
  • social: add discord, linkedin, instagram, facebook, tiktok, pinterest, github, or telegram; absent links are hidden in the footer.
  • footer: columns are i18n-keyed (footer.columns.*); content items carry a section and 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, not dashboard.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 / Placeholder marks (demo.* and placeholder_* 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: socialProofAvatars in packages/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.

On this page