Configuration
Rename the Pulse demo to your product with site-config.ts, and learn where the rest of the project configuration lives.
Launch Now ships as a demo product called Pulse. Its identity (name, tagline, URL, social links)
lives in one file, lib/config/site-config.ts, so turning Pulse into your product is mostly a
one-file change. This page covers that file, the other configuration files, and how to remove the
demo code with pnpm reset-project.
Site config
export const siteConfig = {
/** Display name, shown in the UI, metadata and emails. */
name: "Pulse",
/** Machine name: API key prefix, Stripe lookup keys and product tags. */
slug: "pulse",
tagline: "Product analytics for teams that ship every week.",
description:
"Pulse turns every click, signup and upgrade into answers your whole team can act on.",
url: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
ogImage: "/opengraph-image",
links: {
twitter: "https://x.com/antonio_rnv",
github: "https://github.com/anthonyRanivoarison/app.launch.now",
},
/** Public support address (NEXT_PUBLIC_ so the app sidebar can link to it). */
supportEmail: process.env.NEXT_PUBLIC_SUPPORT_EMAIL,
creator: "@antonio_rnv",
locale: "en_US",
themeColor: "#09090b",
geo: {
region: "FR",
placename: "Paris, France",
latitude: "48.8566",
longitude: "2.3522",
},
}The file reads process.env directly rather than lib/env.ts, so it can be imported from client
components, tsx scripts and builds that run with SKIP_ENV_VALIDATION.
Fields
| Field | Used for |
|---|---|
name | Everywhere the product is named: page titles, auth pages, dashboard, marketing sections, onboarding, emails (appName in Better Auth), manifest, Open Graph image, changelog RSS, legal documents. |
slug | API key prefix (pulse_), Stripe price lookup keys (pulse-pro-monthly) and the app metadata on Stripe products. |
tagline | Homepage title, marketing brand block and Open Graph image. |
description | Default meta description, homepage, manifest, Open Graph image and JSON-LD. |
url | metadataBase, sitemap, robots, changelog RSS, JSON-LD and the product URL in legal pages. Set it with NEXT_PUBLIC_APP_URL. |
links | Social links in the marketing footer and the sameAs list in JSON-LD. |
supportEmail | The "Support" link in the app sidebar. Hidden when NEXT_PUBLIC_SUPPORT_EMAIL is empty. |
creator | Twitter creator in the page metadata. |
locale | Open Graph locale. |
themeColor | Browser theme color in the viewport and web manifest. |
geo | Geo meta tags added by createSeoHead in lib/seo/metadata.ts. |
lib/seo/config.ts re-exports siteConfig for older imports; the source of truth stays
lib/config/site-config.ts.
Rename the product
Update the site config
Change name, slug, tagline, description, links, creator and geo in
lib/config/site-config.ts. Use a lowercase slug without spaces, since it becomes part of API keys
and Stripe lookup keys.
Recreate the Stripe products
The Stripe scripts build their lookup keys from slug. After changing it, run the product script
again so prices exist under the new keys, and copy the printed IDs into .env:
pnpm stripe:productsPlan features that mention the product name come from scripts/seed-plans.ts, so re-run
pnpm db:seed:plans as well.
Changing slug on a live product changes the prefix of new API keys and the lookup keys the
scripts search for. Existing keys and prices keep working, but decide on the slug before launch.
Replace the brand assets
Replace the icons and images in public/ (favicon.ico in app/, favicon-16x16.png,
favicon-32x32.png, apple-touch-icon.png, the android-chrome-* icons, icon.svg, logo_L.svg)
and the marketing screenshots in public/marketing/.
Rewrite the marketing copy
The homepage sections in features/marketing/ (hero, features, steps, testimonials, FAQ, pricing,
CTA) contain copy written for Pulse. See Marketing.
Fill in the legal config
Legal pages read your company details from lib/config/legal.ts. List what is still missing with:
pnpm legal:checkSee Legal.
Other configuration files
next.config.mjs
import { createMDX } from "fumadocs-mdx/next"
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
async redirects() {
return [
{
source: "/organizations",
destination: "/orgs",
permanent: true,
},
]
},
experimental: {
authInterrupts: true,
},
}
const withMDX = createMDX()
export default withMDX(config)createMDX()from Fumadocs compiles the MDX files incontent/for/docsand/blog.authInterruptsenables theforbidden()andunauthorized()functions, rendered byapp/forbidden.tsxandapp/unauthorized.tsx.- The redirect keeps old
/organizationslinks working.
proxy.ts
Next.js 16 renamed Middleware to Proxy. The boilerplate uses it for one feature: when a signed-in user
chose "Always open the app", visiting / redirects to /dashboard.
export const config = {
matcher: [
{
source: "/",
// Must be a literal (statically analyzed); see OPEN_APP_COOKIE.
has: [{ type: "cookie", key: "open_app", value: "1" }],
missing: [{ type: "query", key: "stay" }],
},
],
}The proxy only runs on / when the open_app cookie is set, and it only checks that a session
cookie exists; the dashboard validates the session itself. Add ?stay to the URL to see the homepage
anyway. The cookie is managed in lib/open-app-preference.ts.
components.json
The shadcn/ui config. Components use the base-nova style (built on Base UI), the neutral base
color, CSS variables in app/globals.css and Lucide icons. Aliases point to @/components,
@/components/ui, @/lib, @/lib/utils and @/hooks.
Extra registries are declared so you can install their components with the shadcn CLI:
@loading-ui, @evilcharts, @blocks-so, @shadcn-space, @spell and @spectrumui.
pnpm dlx shadcn@latest add dialogSee Theming for colors and fonts.
drizzle.config.ts
Points drizzle-kit at the schema (drizzle/auth-schema.ts), the migrations folder (drizzle/) and
DATABASE_URL. See Database.
Tooling
| File | Tool |
|---|---|
tsconfig.json | TypeScript. The @/* alias maps to the project root. |
eslint.config.mjs | ESLint, run with pnpm lint. |
.prettierrc | Prettier: no semicolons, double quotes, 80 columns, Tailwind class sorting. |
knip.json | Knip, run with pnpm knip, to find unused files and exports. |
commitlint.config.js | Conventional commits, checked on every commit. |
.release-it.json | Releases. See Deploying. |
All the options are listed in Configuration reference.
Start fresh with reset-project
The boilerplate includes example features that you probably do not want in your product: user-scoped
projects and categories, organization projects generated by the resource kit, a /demo page and the
shared demo account. pnpm reset-project removes them in one go.
pnpm reset-project --dry-run # only print what would happen
pnpm reset-project # ask for confirmation, then apply
pnpm reset-project --yes # no prompt
pnpm reset-project --force # allow a dirty git working treeCommit your work first. The script refuses to run on a working tree with uncommitted changes unless
you pass --force, and it can only run once: it stops if examples/.reset.json exists.
What it does
- Moves these files and folders to
examples/, at the same path:app/(app)/projects,app/(app)/categories,features/projects,features/categories,lib/projects-actions.ts,lib/categories-actions.tsapp/(app)/orgs/[orgSlug]/org-projects,features/org-projects,lib/org-project-actions.ts,lib/org-project-config.ts,drizzle/resources/org-projects.jsonapp/(app)/(dash)/demolib/demo-actions.ts,features/auth/demo-signin-button.tsx,scripts/seed-demo.ts
- Replaces the files in
scripts/reset/templates/(the dashboard content and the organization overview) with clean versions. The originals are copied toexamples/first. - Strips every block between
@reset-remove:startand@reset-remove:endcomments inapp/,components/,features/,lib/anddrizzle/. - Edits config: removes the
db:seed:demoscript frompackage.jsonand theorgProjectentry fromlib/resource-kit/manifest.json. - Writes
examples/.reset.jsonandexamples/README.md, then formats the edited files with Prettier.
The whole plan is computed before anything is written, so an error (such as unbalanced markers)
leaves the project untouched. The examples/ folder is excluded from TypeScript, ESLint and the build.
After the reset
The script prints the next steps:
pnpm typecheck # make sure nothing still imports moved code
pnpm db:generate # creates a migration dropping the example tables
pnpm db:migrate # (skip both on a brand-new database with no data)
pnpm resource:generate # scaffold your own resourcesThen remove DEMO_EMAIL and DEMO_PASSWORD from your .env files, and delete examples/ when you
no longer need it.
Good to know: You can mark your own demo-only code with the same comments, and it will be removed too:
// @reset-remove:start demo-account
import { DemoSignInButton } from "@/features/auth/demo-signin-button"
// @reset-remove:end