App Branding
Trigger
Read this skill whenever the app's name, title, or visual identity is being established or changed — whether the user states it or you derive it yourself.
- Explicit — the prompt names the app: "rename this app to …", "this is a Todo app called …", "call this app …".
- Implicit / agent-generated — the prompt describes what to build but leaves the branding to you: "build me a habit tracker", "turn this into a recipe manager", "make a CRM for my studio". The moment you decide on a title, theme color, or icon from that prompt, treat it as a branding change and apply the full checklist so your generated identity lands on every surface — not just the one screen you happen to be building.
If you are inventing an app title, theme, or icon as part of scaffolding a new app from an initial prompt, that counts — run the checklist in the same pass.
Rule
Apply all of the updates below in a single consistent pass. Depending on the template, the app name may live in one source-of-truth module or be spread across several files — either way, a partial rename leaves the app half-branded (a stale tab title, launcher label, or icon).
Derive three values once, then reuse them everywhere:
- Title — human-readable display name, e.g.
"My Task App". - Slug — URL/npm-safe form: lowercase letters, digits, and hyphens only,
e.g.
"my-task-app". - Description — one sentence describing what the app does.
Locate the layout first
Branding lives in different places depending on the template. Detect which layout you are in before editing:
- Centralized layout —
app/lib/app-config.tsexists. It is the single source of truth for the name and title; the frontend surfaces (<title>, routemeta(), header, sidebar, index heading,apple-mobile-web-app-title) derive from itsAPP_NAME/APP_TITLEexports, so you change the name in one place. - Inlined layout — no
app/lib/app-config.ts. The name and title are written directly intopackage.json, each route'smeta(), andapp/root.tsx. You must update each surface individually.
When in doubt, grep -rn the current title and slug across app/, server/,
package.json, and public/ and fix every real hit — the two layouts share the
manifest and icon steps regardless.
The Checklist
Step 1 — App name + title source
Centralized layout (app/lib/app-config.ts): change both constants and stop —
the rest of the frontend derives from them.
rawAppName→ the slug.rawAppTitle→ the title.
Inlined layout (no app-config.ts): set the title/slug at each surface.
- Every route
meta()title underapp/routes/*.tsx(e.g._index.tsx,agent.tsx,settings.tsx) — replace the app title. These are seeded from a{{APP_TITLE}}placeholder at scaffold time and become plain literals afterward. - Any
useActionQuery("hello", { name: … })inapp/routes/_index.tsxseeded with the title. app/root.tsxapple-mobile-web-app-title— a literal (often"App"); set it to the title.
Step 2 — package.json (both layouts)
displayName→ the title (add the field if the template omits it).description→ the description (add if absent).name→ the slug. Only change this if the user explicitly asks to rename the package (it is the npm package identifier).
Step 3 — Login / auth title (centralized layout only)
If server/plugins/auth.ts exists it keeps its own duplicate rawAppTitle,
used on the login/signup pages. Set it to the same title as
app/lib/app-config.ts. (The inlined layout's server/middleware/auth.ts
carries no app title — skip this step there.)
Step 4 — public/manifest.json (both layouts)
This is a static file (not generated by a plugin). Update:
name→ the title (full install name in OS app drawers).short_name→ a compact launcher label; keep it ≤12 characters, abbreviating the title if needed.description→ the same description used inpackage.json.
Step 5 — Icon SVG files (both layouts)
Replace the icon files with a new mark that reflects the app's identity. The set present depends on the template; update each one you find:
| File | Purpose |
|---|---|
public/favicon.svg |
Browser tab favicon |
public/icon-180.svg |
Apple touch icon (iOS home screen) |
public/icon-192.svg |
PWA install icon (Android / Chrome) |
public/icon-512.svg |
PWA install icon (large / splash) |
Build the icon from SVG primitives — do not embed a base64-encoded PNG. All
files should share the same visual design; only the width and height
attributes differ. A simple, reliable approach: a filled rounded square in the
brand color with the first letter (or a relevant glyph) centered in white.
Template to adapt (change fill, the letter/glyph, and rx to suit the brand):
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
<rect width="192" height="192" rx="40" fill="#2563EB"/>
<text x="96" y="130" font-family="system-ui, sans-serif" font-size="110"
font-weight="700" fill="white" text-anchor="middle">A</text>
</svg>
Keep width/height at 180, 192, and 512 for the respective icon files;
favicon.svg can use any of these sizes (the viewBox keeps it scalable).
What NOT to change
These are stable identifiers or template-internal files, not user-facing brand:
configureTracking({ app, template })inapp/root.tsx— analytics identifiers seeded at scaffold time; leave them. This holds even in the inlined layout, whereappwas seeded from the slug — changing it re-keys analytics history.server/plugins/agent-chat.tsappId(centralized layout) — a stable platform analytics/routing key; leave it exactly as scaffolded..github/workflows/*.yml— template automation; references the repo/template identity, not the running app.AGENTS.md/DEVELOPING.md— developer docs for the template itself.
Verification
pnpm typecheckpasses.- Grep for the old title/slug across
app/,server/,package.json, andpublic/— no unintended leftovers remain (except the intentional "do not change" identifiers above). - The browser tab title, sidebar/header, and index heading all show the new title.