| name | app-branding |
| description | How to rename or rebrand the app so every surface — title, package metadata, PWA manifest, and icons — stays consistent. Use when a prompt establishes or implies a new app name, title, or visual identity, OR when you generate an app title, theme, or icon yourself while scaffolding an app from an initial prompt. |
| scope | dev |
| metadata | {"internal":true} |
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.ts exists. It is the single
source of truth for the name and title; the frontend surfaces (<title>,
route meta(), header, sidebar, index heading, apple-mobile-web-app-title)
derive from its APP_NAME / APP_TITLE exports, so you change the name in one
place.
- Inlined layout — no
app/lib/app-config.ts. The name and title are
written directly into package.json, each route's meta(), and
app/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 under app/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: … }) in app/routes/_index.tsx seeded
with the title.
app/root.tsx apple-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 in package.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 }) in app/root.tsx — analytics
identifiers seeded at scaffold time; leave them. This holds even in the inlined
layout, where app was seeded from the slug — changing it re-keys analytics
history.
server/plugins/agent-chat.ts appId (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 typecheck passes.
- Grep for the old title/slug across
app/, server/, package.json, and
public/ — 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.