Updates
A template you keep taking updates from, not a scaffold you run once.
Your app keeps the kit as a second git remote and takes each update as one commit. No package to install, nothing vendored.
A git graph with two lanes, nuxter-app/kit and your-app. Seven kit commits land on the kit's lane as you scroll, pnpm template:update runs, and they reach your app as one pull request titled chore(template): update to 131069ce30cb. .template-base moves from db3a51ec9e7c to 131069ce30cb. nuxter.app took this exact update on Sep 30, 2026.
01How it works
One remote, one command, one pull request.
The update works out what changed since you last took the kit and applies exactly that.
Once
Add the kit as a remote.
Your app shares none of the kit’s history and needs none of it, so main stays linear.
Each update
Run it on a branch.
It fetches the kit and applies what changed as a single commit.
Review
Read it as a pull request.
The commit lists every kit commit it brings. Reading the diff is the point, and squashing keeps main linear.
Releases
Or take releases.
With no argument the update takes the kit’s main; with a tag, that release. A major tag means you need to act, a minor one applies without your help, and a patch is fixes only.
pnpm template:update v1.4.0A terminal runs each step in turn: “git remote add template https://github.com/nuxter-app/kit.git”; then “git switch -c template-update-$(date +%Y%m%d)” and “pnpm template:update”, which prints “Template 3217573eb935..db3a51ec9e7c is one new commit. Push the branch and open a pull request.”; then “git log -1 --format=%B”, which shows the commit “chore(template): update to db3a51ec9e7c” and the kit commit it brings.
02Ownership
What is yours and what is the kit’s.
Conflicts are proportional to how much of your code lives in the kit’s files. Keeping to this split is the whole discipline.
Yours
Where your code goes. Every
custom/directory is yours, wherever it sits.Shared
Both sides write these.
starter.config.tsconflicts on purpose: a new option appears and you choose its value.The kit’s
Extend by configuration. Editing these is signing up to resolve every conflict.
An update sweeps down your app's file tree. The kit's files are updated: server/auth/, server/payments/, server/tenancy/, server/database/schema.ts, modules/, build/, nuxt.config.ts, emails/. Shared files take both sides' changes: starter.config.ts, package.json, pnpm-lock.yaml, scripts/blog/blog.config.mjs. starter.config.ts stops on a new option, payments.pastDueGraceDays, until you choose its value. Your files stay untouched: app/pages/, app/components/, content/, public/, server/jobs/, server/listeners/, server/database/custom/, server/*/adapters/custom/.
03Seams
Adding is a file or a config entry.
Not an edit to shared code that a later update would conflict on. A new language is the one exception.
- A background job
- file
server/jobs/<kind>.ts - The filename is the kind.
- A listener
- file
server/listeners/<name>.ts - A
00-prefix orders it first. - A table
- file
server/database/custom/schema.ts - Then run
pnpm db:generate. - A provider
- file
server/<subsystem>/adapters/custom/<id>.ts - The filename is its id. One matching a shipped adapter replaces it.
- A payment plan
- key
payments.plans.<plan> - Plus one env var per provider per price. No migration.
- A permission
- key
access.permissions - One string, granted by role or by plan.
- A language
- file
i18n/locales/<code>.json - Plus one
i18n.localesentry, a translated copy of en.json, and an entry in four kit tables. The tests name each one still missing.
A new file, crm-sync.ts, drops into server/jobs/ beside the kit's jobs. In your code, enqueue() now offers “crm-sync” as a job kind, read from the filename. Nothing else needs editing.
04Conflicts
When an update conflicts.
Where both sides changed the same lines, the update stops with conflict markers, as any cherry-pick does, and lists the paths.
git add <path>- Keep your resolution.
git rm <path>- Keep a file you deleted.
git cherry-pick --continue- Finish the update commit.
git cherry-pick --abort- Put everything back.
“pnpm template:update” stops and lists the conflicted files: nuxt.config.ts, i18n/locales/en.json. “git add nuxt.config.ts i18n/locales/en.json” keeps the resolution, “git cherry-pick --continue” finishes, and the update is one new commit.
Tips
Three ways to make a hard update easy.
01
A lockfile conflict
Keep both sides’ entries in package.json, take the kit’s lockfile, and let
pnpm installadd your packages back.02
Delete rather than gut
A deleted file conflicts only as modify/delete, which one
git rmresolves. A hollowed-out one conflicts on every change.03
Take it in pieces
Pass a tag to take one release at a time, or
--no-committo read the whole update staged before you commit it.
The guide
The whole arrangement, in one document.
Starting an app in a monorepo folder, migrations on two chains, releases, and what to do when an update goes badly.