Is your feature request related to a problem? Please describe
In SPA mode (ssr: false), all non-private collections are bundled into a single database.compressed.mjs file that gets downloaded by the browser regardless of which collections are actually needed.
This is especially problematic for i18n/multilingual sites where each locale is a separate collection (e.g., content_en, content_cs, content_sk). A user visiting the Czech version of the site downloads the compressed SQL dumps for ALL languages, even though only the Czech collection will ever be queried. The wasted bandwidth grows linearly with the number of locales and content volume.
This is a broader issue too — any site with multiple collections (e.g., blog, docs, faq) downloads all of them even if the user only navigates to one section.
Describe the solution you'd like
Split database.compressed.mjs into per-collection chunks that are lazy-loaded on first query, similar to how Vite code-splits dynamic imports.
For example, instead of:
// database.compressed.mjs — single monolithic file
export const content_en = "..."
export const content_cs = "..."
export const content_sk = "..."
Generate separate files:
// database.content_en.mjs
export const content_en = "..."
// database.content_cs.mjs
export const content_cs = "..."
And use dynamic import() in the client-side database loader (database.client.js) to fetch only the collection that's being queried:
async function loadCollectionDatabase(collection) {
const { [collection]: compressedDump } = await import(`./database.${collection}.mjs`)
// ... decompress and load into SQLite
}
This way, a user on /cs/playground only downloads the Czech content chunk, not all three languages.
Describe alternatives you've considered
-
private: true on unused collections — This excludes collections from the bundle entirely, but it's a binary on/off. There's no way to say "load this collection lazily on demand." It also requires manual configuration per locale, which doesn't scale.
-
Separate builds per locale — Build the app once per language with only that locale's collection. This works but doubles/triples build time and deployment complexity, and breaks content sharing across locales.
-
Accept the tradeoff — For small sites the total compressed size is manageable, but this doesn't scale as content grows or more locales are added.
-
Use SSR instead of SPA — This avoids the problem entirely since the database stays server-side, but SPA mode is required for certain deployment targets (Capacitor/mobile apps, static hosting without server infrastructure, etc.).
Additional context
- This was a concern in v1 as well (#802) where
db.json could grow very large, but the issue was closed without a solution carried forward to v3.
- The current runtime already loads collections lazily into SQLite WASM (only on first query), but the compressed data is already in the JS bundle — so the network savings don't apply to the initial download.
- Related: Issue #2846 reports localized content queries failing in SPA mode, which is a similar pain point at the intersection of i18n +
ssr: false.
- The per-collection HTTP endpoint (
/__nuxt_content/{collection}/sql_dump.txt) already exists and fetches collections individually at runtime, but this doesn't help with the initial bundle size since all collections are already embedded in database.compressed.mjs.
Is your feature request related to a problem? Please describe
In SPA mode (
ssr: false), all non-private collections are bundled into a singledatabase.compressed.mjsfile that gets downloaded by the browser regardless of which collections are actually needed.This is especially problematic for i18n/multilingual sites where each locale is a separate collection (e.g.,
content_en,content_cs,content_sk). A user visiting the Czech version of the site downloads the compressed SQL dumps for ALL languages, even though only the Czech collection will ever be queried. The wasted bandwidth grows linearly with the number of locales and content volume.This is a broader issue too — any site with multiple collections (e.g.,
blog,docs,faq) downloads all of them even if the user only navigates to one section.Describe the solution you'd like
Split
database.compressed.mjsinto per-collection chunks that are lazy-loaded on first query, similar to how Vite code-splits dynamic imports.For example, instead of:
Generate separate files:
And use dynamic
import()in the client-side database loader (database.client.js) to fetch only the collection that's being queried:This way, a user on
/cs/playgroundonly downloads the Czech content chunk, not all three languages.Describe alternatives you've considered
private: trueon unused collections — This excludes collections from the bundle entirely, but it's a binary on/off. There's no way to say "load this collection lazily on demand." It also requires manual configuration per locale, which doesn't scale.Separate builds per locale — Build the app once per language with only that locale's collection. This works but doubles/triples build time and deployment complexity, and breaks content sharing across locales.
Accept the tradeoff — For small sites the total compressed size is manageable, but this doesn't scale as content grows or more locales are added.
Use SSR instead of SPA — This avoids the problem entirely since the database stays server-side, but SPA mode is required for certain deployment targets (Capacitor/mobile apps, static hosting without server infrastructure, etc.).
Additional context
db.jsoncould grow very large, but the issue was closed without a solution carried forward to v3.ssr: false./__nuxt_content/{collection}/sql_dump.txt) already exists and fetches collections individually at runtime, but this doesn't help with the initial bundle size since all collections are already embedded indatabase.compressed.mjs.