Releasing
Step-by-step guide for publishing a new version of vite-plugin-react-server and updating downstream demo projects. Written for both human maintainers and AI agents.
Prerequisites
- Push access to the plugin repo and demo repos
- npm publish access (requires 2FA — human step)
- Node.js 20+, npm 10+
1. Verify everything passes
The unit/integration suite alone is not sufficient — the plugin can pass its own tests while breaking downstream consumers via cross-condition module leaks (PR #30 is one historical example). You must also verify against the linked demo repos.
cd ~/code/vite-plugin-react-server
# 1a. Full plugin suite — both halves must be green.
npm test # runs test:client and test:server in sequence
# 1b. Build the package.
npm run build
# 1c. Link the local build into each demo and exercise it end-to-end.
# A passing 'npm test' does not exclude regressions that only surface
# when the package is consumed via file:.. from a real downstream app.
for demo in ~/code/bidoof-template ~/code/mmc; do
pushd "$demo"
cp package.json /tmp/$(basename "$demo").pkg.bak
cp -f package-lock.json /tmp/$(basename "$demo").lock.bak 2>/dev/null || true
node -e 'const p=require("./package.json");p.dependencies["vite-plugin-react-server"]="file:../vite-plugin-react-server";require("fs").writeFileSync("./package.json",JSON.stringify(p,null,2)+"\n");'
npm install
npm run build # bidoof-template: build:preview; mmc: build
# Smoke-test the dev server too:
npx vite --port 4173 --strictPort &
VITE_PID=$!
sleep 5
curl -fS http://localhost:4173/ > /dev/null
curl -fS -H "Accept: text/x-component" http://localhost:4173/index.rsc > /dev/null
kill $VITE_PID
# Restore the demo so you don't accidentally commit the file:.. link.
cp /tmp/$(basename "$demo").pkg.bak ./package.json
cp /tmp/$(basename "$demo").lock.bak ./package-lock.json 2>/dev/null || true
npm install
popd
done
# 1d. Playwright e2e (runs against bidoof-template per playwright.config.ts).
npx playwright test test/e2e/hmr.spec.ts
# All 9 tests should pass:
# - page content is visible
# - server component RSC refetch (preserves client state)
# - server component updates todos page
# - useRscHmr listener is active
# - import.meta.hot preserved in library build
# - CSS HMR preserves client state
# - client component does not trigger RSC refetch
# - server action works
# - todo toggle persists
If any of 1a–1d fails, do not publish. File an issue and bisect to the introducing commit. A regression that is already on main but not yet released is still a release-blocker — it ships to consumers the moment you npm publish.
⚠️
file:link masks React version mismatches. Linked demos resolvereact/react-domagainst the plugin's ownnode_modules, which means whatever experimental React is installed insidevite-plugin-react-server/gets hoisted into the consumer for free. A demo that pins stable React 19 in its ownpackage.jsonwill still run against the plugin's experimental React underfile:link, and never exercise the actual stable-React path. To verify a release works for npm consumers — not justfile:-link consumers — alsonpm packthe tarball and install it into a clean fixture before publishing. Skipping this is what let v1.4.6 ship broken on stable React (fixed in PR #32).
2. Bump the version
npm version patch # 1.4.0 → 1.4.1
# or: npm version minor # 1.4.0 → 1.5.0
# or: npm version major # 1.4.0 → 2.0.0
Use --no-git-tag-version if you want to commit and tag manually:
npm version minor --no-git-tag-version
git add package.json package-lock.json
git commit -m "v1.5.0"
git tag v1.5.0
For pre-releases:
npm version 1.5.0-alpha.0
npm publish --tag alpha
3. Push and publish
git push && git push --tags
npm publish # requires 2FA (human step)
4. Update demo projects
For each demo repo (bidoof-template, mmc):
cd ~/code/<demo-repo>
git checkout main && git pull
git checkout -b fix/v<version>
Update the version in package.json:
sed -i 's/"vite-plugin-react-server": "[^"]*"/"vite-plugin-react-server": "^<version>"/' package.json
npm install
Verify the correct version resolved:
grep -A1 '"vite-plugin-react-server"' package-lock.json | head -4
Commit both package.json and package-lock.json — CI uses npm ci which requires them in sync:
git add package.json package-lock.json
git commit -m "chore: bump vite-plugin-react-server to ^<version>"
git push -u origin fix/v<version>
gh pr create --title "chore: bump vite-plugin-react-server to v<version>" \
--body "Bumps vite-plugin-react-server to v<version>."
5. Verify CI
Wait for GitHub Actions to pass on both demo PRs. The workflows will:
- Install dependencies (
npm ci) - Build the project
- Deploy to GitHub Pages (on merge)
If CI fails, check that package-lock.json was committed and the version actually published to npm.
6. Merge demo PRs
Once CI passes, merge the PRs. This triggers the GitHub Pages deploy.
End-to-end verification (optional)
After merging, verify the deployed demos work:
# bidoof-template
curl -s https://nicobrinkkemper.github.io/vite-plugin-react-server-demo-official/ | head -5
# mmc
curl -s https://nicobrinkkemper.github.io/mmc/ | head -5
Local dev testing (linked package)
When developing locally, npm install in demo projects will remove the npm link symlink. After any npm install, re-link:
cd ~/code/<demo-repo>
npm link vite-plugin-react-server
The plugin's configResolved hook auto-creates the react-server-dom-esm symlink in node_modules/ on every Vite startup — no manual step needed.
⚠️ Never run
killall -9 node— this kills Cursor/VS Code's WSL server and crashes the editor. Use targeted kills:lsof -ti:<port> | xargs killorkill %1.
Demo repos
| Repo | GitHub | Deploy |
|---|---|---|
| bidoof-template | nicobrinkkemper/vite-plugin-react-server-demo-official | GitHub Pages |
| mmc | nicobrinkkemper/mmc | GitHub Pages |
Checklist
-
npm test— bothtest:clientANDtest:serverhalves green (no skipped tests added in this release without a tracked issue) -
npm run buildsucceeds -
bidoof-templatelinked viafile:../vite-plugin-react-server—npm run build:previewAND dev server (vite) both succeed; home page returns 200 with content -
mmclinked viafile:../vite-plugin-react-server—npm run buildsucceeds; dev server starts cleanly - Both demo repos restored to their committed
package.json/package-lock.jsonafterwards -
npx playwright test test/e2e/hmr.spec.ts— 9/9 pass -
npm version <type> -
git push && git push --tags -
npm publish(2FA — human step) - For each demo repo:
- Create branch from main
- Update
package.jsonversion -
npm install— commit bothpackage.jsonandpackage-lock.json - Push branch, create PR
- CI passes
- Merge PR