Migrating from nextblogkit

next-blogpanel 1.0.0 is nextblogkit 0.7.x renamed, with configurable paths that actually work and MongoDB GridFS as the default image store. The data model changes in one place; everything else is naming.

Budget about fifteen minutes. Most of it is a find-and-replace.

What changed

Old New
nextblogkit next-blogpanel
npx nextblogkit npx next-blogpanel
nextblogkit.config.ts next-blogpanel.config.ts
NEXTBLOGKIT_* env vars BLOGPANEL_*
NextBlogKitConfig BlogPanelConfig
nbk- CSS classes, --nbk- variables nbp-, --nbp-
Media.r2Key Media.storageKey, plus Media.provider
R2 required for image uploads GridFS by default, R2 optional

BLOGPANEL_, not NEXT_BLOGPANEL_. Next to Next.js's own NEXT_PUBLIC_ convention, a NEXT_-prefixed variable reads as client-exposed, and none of these are.

Steps

# 1. Swap the package
pnpm remove nextblogkit && pnpm add next-blogpanel

# 2. Rename identifiers across your app (review the diff before committing)
FILES=$(git ls-files | grep -E '\.(ts|tsx|css)$')
perl -pi -e 's/NextBlogKitConfig/BlogPanelConfig/g' $FILES
perl -pi -e 's/NEXTBLOGKIT_/BLOGPANEL_/g' $FILES
perl -pi -e 's/nextblogkit/next-blogpanel/g' $FILES
perl -pi -e 's/nbk-/nbp-/g' $FILES

# 3. Rename the config file
git mv nextblogkit.config.ts next-blogpanel.config.ts

# 4. Rename env vars in .env.local (NEXTBLOGKIT_* → BLOGPANEL_*)

# 5. Migrate the media schema — this one is NOT scriptable from your side
npx next-blogpanel migrate

# 6. Generate and wire up the new runtime config module — READ THIS ONE
npx next-blogpanel init
# then add one import line to each of your existing scaffolded files — see
# "Wire up the runtime config module" below. Skipping this breaks every
# image upload.

Step 2 rewrites imports, style imports, class names, custom properties and type annotations in one pass. Run it on a clean working tree so git diff shows you exactly what moved. Step 4 is manual because .env.local is not in git. Step 6 is new in 1.0 and is covered in detail immediately below — it is not optional, even if you are keeping the default paths and would otherwise have no reason to run init again.

Four things the script cannot do

1. Wire up the runtime config module

This is the step most likely to break your upgrade. Read it even if you skim everything else.

1.0 loads next-blogpanel.config.ts at runtime through a generated module, app/next-blogpanel.setup.ts, that npx next-blogpanel init writes next to your app directory and imports from the top of every file it scaffolds. 0.7.x had no such module — your config file existed on disk, but nothing ever imported it, so nothing read it at request time.

Run init once as part of this migration, even if you are keeping the default paths and have no other reason to re-scaffold:

npx next-blogpanel init

init never overwrites a file that already exists (see "Custom paths need a fresh init" below) — and on a 0.7.x upgrade, every route, layout and page it would otherwise scaffold already exists in your tree. So init writes the one genuinely new file, app/next-blogpanel.setup.ts, and then skips every one of your existing files without touching them. None of them ends up importing the new module. init now detects this and prints a warning naming every file it skipped, right at the end of the run where you'll actually see it — but the fix still has to be done by hand.

Add the import yourself, once, to the top of each file. The exact specifier depends on how deep the file sits under app/ — count one ../ for each directory level between the file and your app directory:

// app/blog/page.tsx
import '../next-blogpanel.setup';

// app/blog/[slug]/page.tsx
import '../../next-blogpanel.setup';

// app/api/blog/media/route.ts
import '../../../next-blogpanel.setup';

// app/api/blog/media/file/[...key]/route.ts
import '../../../../../next-blogpanel.setup';

Add it to every file init would have scaffolded fresh: blog/layout.tsx, blog/page.tsx, blog/[slug]/page.tsx, blog/category/[slug]/page.tsx; every file under admin/blog/ (layout.tsx, page.tsx, posts/page.tsx, new/page.tsx, [id]/edit/page.tsx, media/page.tsx, categories/page.tsx, settings/page.tsx); and every route under api/blog/ (posts, media, media/file/[...key], categories, settings, tokens, sitemap.xml, rss.xml). If you used custom paths, substitute them — init's warning lists your actual file paths, not these defaults.

setConfig() installs the config into a process-wide slot, so in practice one import is often enough to cover requests handled by the same server process — Next.js preloads route entries at start, and whichever one runs first installs it for all the others. But you do not control which route that is, so treat that as a safety net, not a plan: add the import everywhere the list above says to.

Skip this and every GridFS upload fails loudly with CONFIG_NOT_INSTALLED rather than silently writing a URL that might be wrong — including for the no-R2 cohort below, who are relying on this migration to get working image storage for the first time.

2. The media schema migration

npx next-blogpanel migrate renames r2Key to storageKey on every media document and backfills provider: 'r2', because everything stored before 1.0 was in R2 by definition. It also drops the superseded r2Key_1 index and rebuilds the index set.

This is not optional. Without it the media library cannot resolve stored files, and deletes have no provider to dispatch on.

It is idempotent — it counts documents still carrying r2Key and does nothing when there are none, so re-running it is safe and is exactly what you want after a partial failure.

3. Custom paths need a fresh init

If you passed --blog-path, --admin-path or --api-path to nextblogkit init, those flags were accepted and then ignored: you got app/blog/, app/admin/blog/ and app/api/blog/ regardless, and fixed it up by hand. In 1.0 the flags drive the scaffolded folder names, the props inside the generated files, and next-blogpanel.config.ts.

Re-run init with the paths you actually want:

npx next-blogpanel init --blog-path /articles --admin-path /admin/articles --api-path /api/articles

Existing files are skipped rather than overwritten, so this will not clobber pages you customised — but it also will not update them. Check the basePath, adminPath and apiPath props in any file you had edited by hand, and delete the old directory tree once the new one works. init warns when it finds a leftover default-path folder.

init now also rejects paths that resolve to the site root, and any two paths that resolve to the same directory.

There is one new scaffolded route with no 0.7.x equivalent: app/api/blog/media/file/[...key]/route.ts, which streams GridFS objects. If you are keeping your existing folders instead of re-running init, add it by hand:

// app/api/blog/media/file/[...key]/route.ts
export { GET } from 'next-blogpanel/api/media-file';

Without it, GridFS-stored images have no URL to be served from.

4. Everyone re-enters their API key once

The admin panel's sessionStorage key changed from nbk_api_key to blogpanel_api_key. The old value is not read and not migrated, so the first visit after upgrading shows the login prompt again. Enter BLOGPANEL_API_KEY and it is remembered for the session as before.

Nothing is lost — the key lives in your environment, not in the browser.

Your data does not move

MongoDB collection names are unchanged: nbk_posts, nbk_categories, nbk_media, nbk_settings, nbk_api_tokens. That prefix is live user data, and renaming collections would mean a migration with real downside and no benefit. Posts, categories and settings are untouched by the upgrade; only the media documents change shape, and only in the two fields above.

Issued API tokens also keep their nbk_ prefix. Tokens are stored as SHA-256 hashes, so existing ones keep validating and integrations that hold one keep working. Newly generated tokens use the same prefix for consistency. It is legacy naming, kept deliberately.

Storage after the upgrade

If you were running R2, nothing changes. Keep the five variables — renamed to BLOGPANEL_R2_* — and R2 stays the backend. Existing media documents are marked provider: 'r2' by the migration, so their files are found and deleted correctly.

If you were running without R2, you now have real image storage for the first time. The blob-URL placeholder that the 0.7.x editor fell back to is no longer the practical outcome of an unconfigured install: GridFS is the default backend and it needs no configuration. Uploads persist — but only once the runtime config module is wired up. This is the cohort step 1 above matters most for: skip it, and every one of these newly-working uploads fails with CONFIG_NOT_INSTALLED instead of silently writing a bad URL.

To move from R2 to GridFS, set BLOGPANEL_STORAGE=gridfs. New uploads go to MongoDB while old ones stay in R2 and remain reachable — each document remembers its own provider. The package does not copy existing objects across.

After migrating

npx next-blogpanel health   # MongoDB, R2 and environment variables
grep -rn "nextblogkit\|NEXTBLOGKIT_\|nbk-" src/ app/   # should return nothing

Then open the admin panel, load the media library, and confirm the thumbnails render. That single check exercises the renamed schema, the provider field and the storage route at once.