[{"data":1,"prerenderedAt":567},["ShallowReactive",2],{"blog-nuxt-contentv3-vercel-issue":3},{"id":4,"title":5,"body":6,"date":552,"description":553,"extension":554,"meta":555,"navigation":556,"path":557,"seo":558,"stem":559,"tags":560,"__hash__":566},"blog/blog/nuxt-contentv3-vercel-issue.md","Deploying Nuxt Content v3 on Vercel: The `better-sqlite3` Trap",{"type":7,"value":8,"toc":536},"minimark",[9,14,27,62,65,68,76,93,107,109,113,123,210,220,222,229,243,246,260,263,271,324,330,334,337,353,356,365,372,375,420,426,428,432,438,444,456,458,462,529,532],[10,11,13],"h2",{"id":12},"the-trap","The Trap",[15,16,17,18,22,23,26],"p",{},"If you've recently started a project with ",[19,20,21],"strong",{},"Nuxt 4"," and ",[19,24,25],{},"Nuxt Content v3"," and tried to deploy it to Vercel, you may have run into a frustrating 500 error that looks something like this:",[28,29,34],"pre",{"className":30,"code":31,"language":32,"meta":33,"style":33},"language-bash shiki shiki-themes github-light github-dark","Module did not self-register:\n'/var/task/node_modules/better-sqlite3/build/Release/better_sqlite3.node'\n","bash","",[35,36,37,56],"code",{"__ignoreMap":33},[38,39,42,46,50,53],"span",{"class":40,"line":41},"line",1,[38,43,45],{"class":44},"sScJk","Module",[38,47,49],{"class":48},"sZZnC"," did",[38,51,52],{"class":48}," not",[38,54,55],{"class":48}," self-register:\n",[38,57,59],{"class":40,"line":58},2,[38,60,61],{"class":44},"'/var/task/node_modules/better-sqlite3/build/Release/better_sqlite3.node'\n",[15,63,64],{},"I hit this exact wall, and this post is a breakdown of what went wrong, why it happened, and the clean solution that actually works.",[66,67],"hr",{},[10,69,71,72,75],{"id":70},"what-is-better-sqlite3-and-why-is-it-a-problem","What Is ",[35,73,74],{},"better-sqlite3"," and Why Is It a Problem?",[15,77,78,79,81,82,84,85,88,89,92],{},"Nuxt Content v3 uses SQLite under the hood to index and query your markdown content. Locally, it uses ",[35,80,74],{}," — a native Node.js addon — to do this. \"Native\" is the key word here. Unlike a pure JavaScript package, ",[35,83,74],{}," is compiled C++ code that produces a ",[35,86,87],{},".node"," binary file. This binary is compiled at install time and is tightly coupled to the specific OS, architecture, and Node.js version on the machine where ",[35,90,91],{},"npm install"," was run.",[15,94,95,96,99,100,102,103,106],{},"Vercel's build environment compiles the binary just fine during the build step. The problem is that ",[19,97,98],{},"Vercel runs your deployed code inside serverless Lambda functions",", which have a different runtime environment than the build machine. The pre-compiled ",[35,101,87],{}," binary simply does not work there, and Node.js throws ",[35,104,105],{},"ERR_DLOPEN_FAILED"," — the \"Module did not self-register\" error you see in the logs.",[66,108],{},[10,110,112],{"id":111},"what-i-was-doing-wrong","What I Was Doing Wrong",[15,114,115,116,119,120,122],{},"My ",[35,117,118],{},"package.json"," had ",[35,121,74],{}," listed as a direct dependency:",[28,124,128],{"className":125,"code":126,"filename":118,"language":127,"meta":33,"style":33},"language-json shiki shiki-themes github-light github-dark","\"dependencies\": {\n  \"@nuxt/content\": \"^3.11.2\",\n  \"better-sqlite3\": \"^12.6.2\",\n  \"nuxt\": \"^4.3.1\",\n  \"vue\": \"^3.5.28\",\n  \"vue-router\": \"^4.6.4\"\n}\n","json",[35,129,130,139,154,167,180,193,204],{"__ignoreMap":33},[38,131,132,135],{"class":40,"line":41},[38,133,134],{"class":48},"\"dependencies\"",[38,136,138],{"class":137},"sVt8B",": {\n",[38,140,141,145,148,151],{"class":40,"line":58},[38,142,144],{"class":143},"sj4cs","  \"@nuxt/content\"",[38,146,147],{"class":137},": ",[38,149,150],{"class":48},"\"^3.11.2\"",[38,152,153],{"class":137},",\n",[38,155,157,160,162,165],{"class":40,"line":156},3,[38,158,159],{"class":143},"  \"better-sqlite3\"",[38,161,147],{"class":137},[38,163,164],{"class":48},"\"^12.6.2\"",[38,166,153],{"class":137},[38,168,170,173,175,178],{"class":40,"line":169},4,[38,171,172],{"class":143},"  \"nuxt\"",[38,174,147],{"class":137},[38,176,177],{"class":48},"\"^4.3.1\"",[38,179,153],{"class":137},[38,181,183,186,188,191],{"class":40,"line":182},5,[38,184,185],{"class":143},"  \"vue\"",[38,187,147],{"class":137},[38,189,190],{"class":48},"\"^3.5.28\"",[38,192,153],{"class":137},[38,194,196,199,201],{"class":40,"line":195},6,[38,197,198],{"class":143},"  \"vue-router\"",[38,200,147],{"class":137},[38,202,203],{"class":48},"\"^4.6.4\"\n",[38,205,207],{"class":40,"line":206},7,[38,208,209],{"class":137},"}\n",[15,211,212,213,216,217,219],{},"I had added it manually, assuming Nuxt Content needed me to provide it explicitly. This was the wrong assumption. Nuxt Content manages its own database adapter internally and is designed to select the right one based on the deployment target — ",[19,214,215],{},"but only if you let it",". By forcing ",[35,218,74],{}," into my own dependencies, I was overriding that behavior and locking the deployment into using a native binary that cannot survive in a serverless environment.",[66,221],{},[10,223,225,226],{"id":224},"the-solution-static-generation-with-nuxt-generate","The Solution: Static Generation with ",[35,227,228],{},"nuxt generate",[15,230,231,232,235,236,238,239,242],{},"The correct approach for deploying a Nuxt Content site on Vercel — especially a portfolio or blog — is to ",[19,233,234],{},"statically pre-render your site at build time"," using ",[35,237,228],{}," instead of ",[35,240,241],{},"nuxt build",".",[15,244,245],{},"Here is what changes and why it works:",[247,248,249,255],"ul",{},[250,251,252,254],"li",{},[35,253,241],{}," produces a server-rendered app that needs a running Node.js server to handle each request. That server needs SQLite at runtime — which is where the crash happens.",[250,256,257,259],{},[35,258,228],{}," pre-renders every page into plain HTML files at build time. By the time the site is deployed, there is no SQLite needed at runtime. The content has already been read, processed, and baked into static files.",[15,261,262],{},"Vercel then serves these files straight from its CDN — no serverless function, no native binary, no crash.",[264,265,267,268,270],"h3",{"id":266},"step-1-remove-better-sqlite3-from-your-dependencies","Step 1: Remove ",[35,269,74],{}," from your dependencies",[28,272,274],{"className":125,"code":273,"filename":118,"language":127,"meta":33,"style":33},"\"dependencies\": {\n  \"@nuxt/content\": \"^3.11.2\",\n  \"nuxt\": \"^4.3.1\",\n  \"vue\": \"^3.5.28\",\n  \"vue-router\": \"^4.6.4\"\n}\n",[35,275,276,282,292,302,312,320],{"__ignoreMap":33},[38,277,278,280],{"class":40,"line":41},[38,279,134],{"class":48},[38,281,138],{"class":137},[38,283,284,286,288,290],{"class":40,"line":58},[38,285,144],{"class":143},[38,287,147],{"class":137},[38,289,150],{"class":48},[38,291,153],{"class":137},[38,293,294,296,298,300],{"class":40,"line":156},[38,295,172],{"class":143},[38,297,147],{"class":137},[38,299,177],{"class":48},[38,301,153],{"class":137},[38,303,304,306,308,310],{"class":40,"line":169},[38,305,185],{"class":143},[38,307,147],{"class":137},[38,309,190],{"class":48},[38,311,153],{"class":137},[38,313,314,316,318],{"class":40,"line":182},[38,315,198],{"class":143},[38,317,147],{"class":137},[38,319,203],{"class":48},[38,321,322],{"class":40,"line":195},[38,323,209],{"class":137},[15,325,326,327,329],{},"Run ",[35,328,91],{}," to update your lockfile after this change.",[264,331,333],{"id":332},"step-2-change-your-vercel-build-command","Step 2: Change your Vercel build command",[15,335,336],{},"In your Vercel project settings, set the build command to:",[28,338,340],{"className":30,"code":339,"language":32,"meta":33,"style":33},"npm run generate\n",[35,341,342],{"__ignoreMap":33},[38,343,344,347,350],{"class":40,"line":41},[38,345,346],{"class":44},"npm",[38,348,349],{"class":48}," run",[38,351,352],{"class":48}," generate\n",[15,354,355],{},"And set the output directory to:",[28,357,359],{"className":30,"code":358,"language":32,"meta":33,"style":33},".output/public\n",[35,360,361],{"__ignoreMap":33},[38,362,363],{"class":40,"line":41},[38,364,358],{"class":44},[264,366,368,369],{"id":367},"step-3-optional-explicitly-set-the-preset-in-nuxtconfigts","Step 3 (Optional): Explicitly set the preset in ",[35,370,371],{},"nuxt.config.ts",[15,373,374],{},"You can also make the static target explicit in your config:",[28,376,380],{"className":377,"code":378,"filename":371,"language":379,"meta":33,"style":33},"language-ts shiki shiki-themes github-light github-dark","export default defineNuxtConfig({\n  nitro: {\n    preset: 'static'\n  }\n})\n","ts",[35,381,382,397,402,410,415],{"__ignoreMap":33},[38,383,384,388,391,394],{"class":40,"line":41},[38,385,387],{"class":386},"szBVR","export",[38,389,390],{"class":386}," default",[38,392,393],{"class":44}," defineNuxtConfig",[38,395,396],{"class":137},"({\n",[38,398,399],{"class":40,"line":58},[38,400,401],{"class":137},"  nitro: {\n",[38,403,404,407],{"class":40,"line":156},[38,405,406],{"class":137},"    preset: ",[38,408,409],{"class":48},"'static'\n",[38,411,412],{"class":40,"line":169},[38,413,414],{"class":137},"  }\n",[38,416,417],{"class":40,"line":182},[38,418,419],{"class":137},"})\n",[15,421,422,423,425],{},"This is optional since ",[35,424,228],{}," already implies static output, but it makes your intent clear and ensures consistent behavior across environments.",[66,427],{},[10,429,431],{"id":430},"what-to-keep-in-mind","What to Keep in Mind",[15,433,434,437],{},[19,435,436],{},"Re-deploying on new content:"," With static generation, your site is a snapshot taken at build time. If you add or edit a blog post, you need to trigger a new deployment. On Vercel this is trivial — just push to your repository and it redeploys automatically. For a portfolio or blog this is almost always perfectly acceptable.",[15,439,440,443],{},[19,441,442],{},"Dynamic content:"," If your site ever needs truly dynamic content that changes at runtime without a redeploy (like user-submitted data or live comments), static generation is not sufficient and you would need a different hosting strategy. For most personal sites and blogs, though, static is the right default.",[15,445,446,449,450,452,453,455],{},[19,447,448],{},"The Nuxt Content documentation says \"no extra config needed for Vercel\""," — and that is true, but it assumes you are using ",[35,451,228],{},". The docs are written with the static use case in mind for serverless platforms like Vercel. Using ",[35,454,241],{}," in a serverless context is what causes the conflict.",[66,457],{},[10,459,461],{"id":460},"summary","Summary",[463,464,465,481],"table",{},[466,467,468],"thead",{},[469,470,471,475,478],"tr",{},[472,473,474],"th",{},"Approach",[472,476,477],{},"What Happens",[472,479,480],{},"Works on Vercel?",[482,483,484,501,515],"tbody",{},[469,485,486,495,498],{},[487,488,489,491,492,494],"td",{},[35,490,241],{}," + ",[35,493,74],{}," in deps",[487,496,497],{},"Native binary crashes in Lambda",[487,499,500],{},"❌",[469,502,503,510,513],{},[487,504,505,507,508],{},[35,506,241],{}," without ",[35,509,74],{},[487,511,512],{},"Build prompt hangs asking to install it",[487,514,500],{},[469,516,517,523,526],{},[487,518,519,507,521],{},[35,520,228],{},[35,522,74],{},[487,524,525],{},"Pages pre-rendered at build time, no runtime SQLite needed",[487,527,528],{},"✅",[15,530,531],{},"The fix is genuinely simple once you understand why the problem exists. Nuxt Content is well-designed for static deployment — you just have to let it do its job by using the right build command and not fighting it with manual native dependencies.",[533,534,535],"style",{},"html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":33,"searchDepth":58,"depth":58,"links":537},[538,539,541,542,550,551],{"id":12,"depth":58,"text":13},{"id":70,"depth":58,"text":540},"What Is better-sqlite3 and Why Is It a Problem?",{"id":111,"depth":58,"text":112},{"id":224,"depth":58,"text":543,"children":544},"The Solution: Static Generation with nuxt generate",[545,547,548],{"id":266,"depth":156,"text":546},"Step 1: Remove better-sqlite3 from your dependencies",{"id":332,"depth":156,"text":333},{"id":367,"depth":156,"text":549},"Step 3 (Optional): Explicitly set the preset in nuxt.config.ts",{"id":430,"depth":58,"text":431},{"id":460,"depth":58,"text":461},"2025-12-23","While deployment of a project made in NUXT4 with NUXT Content v3, I ran into an issue where it was running perfectly fine in localhost but when I deployed it on vercel, I was faced with an error which I wasn't able to solve for a good amount of time. So in this blog I have documented how I solved that error and finally deployed my project.","md",{},true,"/blog/nuxt-contentv3-vercel-issue",{"title":5,"description":553},"blog/nuxt-contentv3-vercel-issue",[561,562,563,564,565],"devlog","nuxt","nuxt content","vercel","issue","t1dIZLNoDBcokDNLqM0UMj0iXnigU1ClDF8NGj-s9kM",1771920105857]