SPA
Running Nuxt as a single-page application (SPA) is pretty straightforward, but there are a couple of challenges.
Configuration
Enable SPA mode by simply setting the ssr
config to false
. This disables
server-side rendering.
export default defineNuxtConfig({
...
ssr: false,
...
});
Build
Apparently, nuxi build
only works if you have Node.js in the backend. Since it
is a single page application, we shouldn't require it on the server side. To
statically serve an SPA from any HTTP server, we'll use nuxi generate
like we
do in static sites.
{
...
"scripts": {
...
"build": "nuxi generate",
...
},
...
}
We named it as
build
notgenerate
, because for us there is no use case where we require bothnuxi generate
andnuxi build
. Sincebuild
is a general term for building the application for production, we stuck with that.
Loading Page
By disabling ssr
, nuxi generate
no more generates static pages per route.
However, it still generates an index.html
at the root, which will load the app
on the client-side. To inform user with a loading page, Nuxt allows you to show
a loading page in the index.html
by placing a file called
spa-loading-template.html
in ./app
folder at the root of your project. See
Nuxt Configuration / spaLoadingTemplate for the detailed explanation and a
sample template.
- . (root)
|_ /app
|_ spa-loading-template.html
Notice this template file is only partial. Nuxt will render it within the generated
index.html
file. So, don't wrap it with<html>
and<body>
tags.
Deploy
After the nuxi generate
script runs, it outputs the static files under
.output/public
by default. When you configure your server to serve those
files, you are done.
Below you can see a Dockerfile
that builds an app under src/my-app
folder,
and then deploy and serve it using an nginx
server instance.
# syntax=docker/dockerfile:1.7-labs
FROM node:22-bullseye AS build
# first copy only package.json and package-lock.json to cache after downloading
# packages
COPY --parents src/my-app/package.json .
COPY --parents src/my-app/package-lock.json .
RUN cd src/my-app ; npm install
# copy rest of the app while keeping the folder structure
COPY --parents src/my-app/ .
RUN cd src/my-app ; npm run build:development
FROM nginx AS final
# bring output from `build` phase
COPY --from=build src/my-app/.output/public /www/public
# use an inline nginx config to serve static files
RUN cat <<EOF > /etc/nginx/nginx.conf
events { }
http {
# add output files as root for nginx server
root /www/public;
server {
listen 80;
# include default mime types for js and css files to have proper content
# type
location / {
include /etc/nginx/mime.types;
}
}
}
EOF
CMD nginx -g "daemon off;"