Introduction
With users accessing content from every corner of the globe, most servers fall short in delivering low-latency and high-performance experiences.
Thanks to Edge computing! Through Content Delivery Networks (CDNs), the Edge allows JavaScript to execute closer to end users.
Nuxt.js, a powerful Vue.js framework allows developers to gain a complete toolkit for building applications that can be deployed anywhere, including on edge platforms like Cloudflare Workers, Netlify Edge Functions, and Vercel through its Nitro engine.
This article explores how to build and deploy a Nuxt.js application at the edge while integrating Strapi for content management and authentication.
Video Resource
The complete video for this tutorial is available on YouTube.
In this video, Sebastien Chopin, the creator of Nuxt.js, explores the concept, benefits, and challenges of building for the edge.
What Is Edge Computing in JavaScript?
At its core, the Edge refers to a limited JavaScript runtime that runs on CDN nodes, which are used to serve static files. However, instead of just HTML and static files, you can now execute JavaScript at these edge locations.
What this means is that you can execute JavaScript where normally you would serve static files.
Let's discuss the benefits of using the Edge.
Key Benefits of Running Nuxt on the Edge
Here are some of the benefits of the Edge.
- Runs milliseconds from end users (low latency)
- Zero milliseconds cold start (no need to spawn virtual machines like in traditional serverless).
- Request and context isolation thanks to the V8 engine. Thus, no shared state between requests.
- Highly affordable. For example, you can make 100,000 free requests/day via Cloudflare. This is cheaper compared to serverless.
Common Limitations of JavaScript Edge Runtimes
The Edge also has its limitations which include:
- Subset of Node.js and Browser API: There is no Node.js API support (limited access), no DOM manipulation, no HTTP server, no file system, etc. However, different providers are slowly supporting some Node.js APIs.
- Small server size limit: You can't upload more than 10 MB gzip of JavaScript.
Top Edge Platforms for Deploying Nuxt.js Applications
You can deploy Nuxt.js to Cloudflare workers or other platforms once you have built your app.
After deploying your code to an Edge platform, your code will be replicated on many nodes. You can think of it like a load balancer but every node will be running your website.
Here are some Edge platforms you can deploy to:
- AWS Lambda@Edge,
- Bunny.net,
- Cloudflare Pages/Workers,
- Vercel Edge Functions,
- Deno Deploy,
- Fastly Comput,
- Netlify Edge Functions,
- Vercel Edge Functions,
- etc.
As we mentioned above, the 2 limitations of Edge runtimes can pose a big issue. So, 3 years ago, Nuxt 3 was being developed to ensure you could deploy a Nuxt website to edge runtimes.
So, what is Nuxt.js?
What Is Nuxt.js? Vue Framework for Full-Stack Web Apps
Nuxt is a progressive web framework used to create full-stack applications with TypeScript and Vue.js.
It was created by Sebastian Chopin in October 2016. So far, it has achieved the following:
- 57K+ stars on GitHub.
- 3.3M+ downloads/month.
- Used by startups and enterprises alike.
Top Features of Nuxt.js for Building Full-Stack Apps
Some features found in Next.js are also available in Nuxt.js. They include the following:
- Server-Side Rendering: Nuxt provides you with server-side rendering with the option of disabling it. For example, you can disable it globally if you are just building a dashboard or at the root level for example:
1export default defineNuxtConfig({
2 routeRules: {
3 '/dashboard/**': { ssr: false }
4 }
5})
- Pre-rendering: Nuxt.js supports pre-rendering to allow you to pre-render your whole website. Nuxt has an internal crawler that crawls your website to find links and generate them. Or you can specify the routes you want to render at built time or dynamically.
- Routing and Layouts with automatic code splitting: Nuxt will ship the minimum amount of JavaScript, and then as you navigate further, more JavaScript will be downloaded.
- Data fetching at the component level: Just as you can do suspense with Vue.js, you can do data fetching at the component level using Nuxt. This is very useful when you want to reuse data fetching and not be limited to the page level.
- Protected routes: You can protect routes using Nuxt.js Middleware.
- State management: Nuxt.js provides you with a basic state management composable even though there are plugins to provide you with State management. This allows you to share data between page components.
- Server API routes: You can create API routes using Nuxt. However, you don't need to do this if you are using Strapi as a Headless CMS.
- Layer system: Nuxt allows you to create a Nuxt app within a Nuxt app using the layers directory. This means that Nuxt provides you with a domain-driven design for a specific feature using layers or NPM packages. For example, an e-commerce, where you can have a cart layer, shop layer, product layer, and so on. Layers allow each team to work on a specific directory giving you the freedom to run only one specific layer depending on your choice.
- Module Ecosystem (Nuxt Plugins): You can extend your Nuxt applications using modules. There are over 270+ modules or plugins currently that allow you to extend the features of Nuxt or integrate into various services.
- Deploy Anywhere: You can deploy your Nuxt app anywhere. There is no vendor lock-in.
Essential Nuxt Modules and How to Integrate Strapi
Nuxt also comes with its own official modules so as to keep it as light as possible. These include the following:
- @nuxt/content: This allows you to work with Markdowns especially if you are working on documentation, a portfolio, etc. This is basically a file-based CMS.
- @nuxt/eslint: Linting integration.
- @nuxt/fonts: Allows you to quickly add custom web fonts from Google or other providers with performance in mind.
- @nuxt/icon: Gives you access to over 200,000 icons via Iconify.
- @nuxt/image: Great for adding images. Ensures performance and provides support for many providers.
- @nuxt/scripts: Developed with the Google Chrome Aurora team to improve script performance (e.g., A/B testing scripts) without slowing down the site).
- @nuxt/ui: Useful development tools and UI components built on Radix UI and Tailwind CSS.
How to Install and Configure the Strapi Module in Nuxt.js
You can install any of the modules above using the command below:
npx nuxt module add <module>
Step 1: Install Strapi Nuxt Module
There is the Strapi module for Nuxt. You can install it using the command below:
npm nuxt module add strapi
Step 2: Set Strapi URL in the Environment Variable
The next step is to set the Strapi URL in the environment variable:
STRAPI_URL=http://localahost:1337
Step 3: Start Using the Strapi Vue Composables:
After installation, you can use the Vue composable in your code. The Nuxt module for Strapi integration supports authentication, fetching of collections, and so on.
1const { find } = useStrapi();
2const { login } = useStrapiAuth();
3const user = useStrapiAuth();
4
5// Example: fetch restaurants
6const restaurants = await find(`restaurants`);
7
8// Example: login
9await login({
10 identifier: "test@test.com",
11 password: "password",
12});
You can learn more about Strapi and Nuxt integration in these documentation links.
How to Deploy Nuxt.js Apps to Edge Platforms like Cloudflare and Netlify
In order to make sure to support Nuxt.js edge deployment, Nuxt provides proxies or polyfills for Node.js dependencies automatically.
The Nuxt build output is optimized for runtime compatibility.
For example when you have a basic Nuxt application and you build for Cloudflare using the command below.
1npx nuxt build --preset=cloudflare-module
The command is used to deploy Nuxt.js to Cloudflare Workers.
Here is what Nuxt will do with the command:
- Create an output directory.
- Inside the output directory, you will find your whole application with the server, components, routes, etc. included.
- Produces a small build: ~700KB raw, ~227KB gzipped.
- Supports cold starts of 4ms or less by creating context isolation and will start your Nuxt application server every time. This means that if a user calls your API, it won't load all your bundle, so as to make it as fast as possible.
Nuxt.js Build Presets For the Edge
How do you build for different Edge platforms? The Nuxt.js team has worked on a server engine so you don't have to worry.
You can use the preset for different platforms as shown below:
- For Cloudflare Workers
1nuxt build --preset-cloudflare_module
- For Deno Deploy
1nuxt build --preset-deno_deploy
- For Netflify Edge Functions
1nuxt build --preset-netlify_edge
- For Vercel Edge Function
1nuxt build --preset-vercel_edge
An example of how you would want to deploy to Cloudflare would look like this:
1// Generate output format
2export default {
3 async fetch(request){
4 // Your Nuxt code
5 }
6}
Example: Server-Side Rendering with Nuxt.js with Dynamic Styling
Let's demonstrate Server-side rendering with Nuxt.js.
Say you create a basic Nuxt.js application that contains:
- A main component (
app.vue
) - A public folder for static assets
- A basic Nuxt configuration.
Render a random color each time the page is refreshed to demonstrate server-side rendering (SSR) in Nuxt.
1// Path: ./app.vue
2
3<script setup>
4const colors = [
5 "red",
6 "green",
7 "blue",
8 "yellow",
9 "purple",
10 "orange",
11 "pink",
12 "brown",
13];
14
15const color = colors[Math.floor(Math.random() * colors.length)];
16</script>
17
18<template>
19 <h1 :style="{ color }">Hello Strapi!</h1>
20</template>
Here is what the page looks like. When you refresh, you will have a random color assigned to the title.
Fixing Hydration Errors in Nuxt Server-Side Rendering
Due to Nuxt being a universal framework, it will server-render your application. Hydration errors can occur when the client and server render different initial values. So, when you navigate to your browser dev tool, you should see an error as shown below:
The error is that we have a different color happening on the client side and server side, a "blue" and "green" respectively. This is called hydration. So let's make this render only on the client side.
The solution for this would be to use a shared state value between client and server, similar to states in React or Next.js.
Here is the updated code:
1<script setup>
2const colors = [
3 "red",
4 "green",
5 "blue",
6 "yellow",
7 "purple",
8 "orange",
9 "pink",
10 "brown",
11];
12
13const color = useState(
14 "color",
15 () => colors[Math.floor(Math.random() * colors.length)]
16);
17</script>
18
19<template>
20 <h1 :style="{ color }">Hello Strapi!</h1>
21</template>
The useState
state management creates a variable that will be created by the server and reused by the client.
Let's deploy this basic app to the Edge.
Deploying Nuxt.js App to Cloudflare Edge
Run the command below to create the build for Cloudflare Pages:
1npx nuxt build --preset=cloudflare-module
To deploy to production, run the command below:
1npx wrangler deploy .output
The command installs wrangler which is for Cloudflare deployment.
Upon successful Nuxt.js Edge deployment, you should have a URL of your Nuxt project, with SSR on the Edge. And you should see that the app responded globally in less than 200ms. And you don't need to manage servers, as it automatically scales. Most importantly, it is closer to users.
Learn more about about deploying to Cloudflare.
How to Add Custom Fonts in Nuxt Using @nuxt/fonts
Module
Let's make our basic app fancier by installing the nuxt-fonts
module.
Run the command below inside your Nuxt application:
1npx nuxt module add fonts
Once that is done, add the following styles to your code inside the app.vue
file.
1// Path: ./app.vue
2
3<script setup>
4const colors = [
5 "red",
6 "green",
7 "blue",
8 "yellow",
9 "purple",
10 "orange",
11 "pink",
12 "brown",
13];
14
15const color = useState(
16 "color",
17 () => colors[Math.floor(Math.random() * colors.length)]
18);
19</script>
20
21<template>
22 <h1 :style="{ color }">Hello Strapi!</h1>
23</template>
24
25<style>
26body {
27 font-family: "Pacifico", sans-serif;
28}
29</style>
This is what the font of your app should look like.
Now, when you build your Nuxt application using npm run build
command, you will see that Nuxt will automatically download the fonts and store it. This means there won't be any API call to Google fonts. See image below.
Full Example: Implementing Strapi Authentication in Nuxt.js
Now, let's demonstrate how to integrate Strapi authentication using the nuxtjs/strapi module. Let's leverage Strapi authentication!
Step 1: Install the Strapi Module
Install the Strapi module developed by the Nuxt team by running the command below. Note that so far, the Strapi module didn't leverage the Strapi client library.
npx nuxt module add strapi
Step 2: Add Strapi URL to Nuxt.js .env
Add your Strapi API URL to the .env
file of your Nuxt.js app and ensure your Strapi app is running.
STRAPI_URL=http://localhost:1337
Step 3: Display Authenticated User and Logout Button
To display an authenticated user, we will have to first get the user using the useStrapiUser()
function. To check if a user is logged in, we will get the auth information using the useStrapiAuth()
function.
1// Path: ./.env
2
3// ... color variable and useState
4
5const auth = useStrapiAuth();
6const user = useStrapiUser();
7
8<template>
9 <h1 :style="{ color }">Hello Strapi!</h1>
10 <div v-if="user">
11 <pre >{{ user }}</pre>
12 <button @click="auth.logout">Logout</button>
13 </div>
14
15// ... other codes
16</template>
17
18// ... styles
In the code above, we did the following:
- Display the user if the user is authenticated.
- Add a button to log out the user.
When you refresh your Nuxt app, you will notice that the user is not authenticated. So, let's add a way to authenticate the user by creating a Sign-up form.
Step 4: Create Nuxt.js Form for User Signup
Create a credentials object that should contain username
, email
, and password
variables for a user to sign up.
1// Path: ./app.vue
2
3// ... other codes
4
5const credentials = reactive({
6 username: "",
7 email: "",
8 password: "",
9});
10
11// ... other codes
Next, create a form to be displayed if the user is not authenticated. This should call the handleSignUp
function which will handle the form submission. Ensure you pass in the credentials to the appropriate form inputs.
1// Path: ./app.vue
2
3// ... other codes
4
5<div v-else>
6 <form @submit.prevent="handleSignUp">
7 <h2>Signup</h2>
8 <input
9 v-model="credentials.username"
10 type="text"
11 placeholder="Username"
12 required
13 />
14 <input
15 v-model="credentials.email"
16 type="email"
17 placeholder="Email"
18 required
19 />
20 <input
21 v-model="credentials.password"
22 type="password"
23 placeholder="Password"
24 required
25 />
26 <button type="submit">Signup</button>
27 </form>
28</div>
29
30// ... other codes
Now, this is what your app should look like:
Step 5: Create Nuxt.js Signup Function
Next, create the handleSignUp()
function above to handle the signup form submission.
1// Path: ./app.vue
2
3// ... other codes
4
5async function handleSignUp() {
6 await auth.register(credentials).catch((err) => alert(err.error?.message));
7}
8
9// ... other codes
Now, let's sign up a new user.
As you can see we have signed up a new user in the Strapi backend.
Now, let's log in as a user.
Step 6: Create Nuxt.js Login Form
Create a login form for users to enter their identifier and password.
1// Path: ./app.vue
2
3// ... other codes
4<form @submit.prevent="handleLogin">
5 <h2>Login</h2>
6 <input
7 v-model="credentials.username"
8 type="text"
9 placeholder="Username or Email"
10 required
11 />
12 <input
13 v-model="credentials.password"
14 type="password"
15 placeholder="Password"
16 required
17 />
18 <button type="submit">Login</button>
19</form>
20
21
22// ... other codes
Create Nuxt.js Login Form
Create a function to handle the login form submission.
1// Path: ./app.vue
2
3// ... other codes
4
5async function handleLogin() {
6 await auth
7 .login({
8 identifier: credentials.username,
9 password: credentials.password,
10 })
11 .catch((err) => alert(err.error?.message));
12}
13
14// ... other codes
Now, we can log in as a user as shown below:
Complete Code: GitHub Repo
Here is the complete code for our basic Nuxt app which we integrated with Strapi using the Nuxt Strapi module.
1// Path: ./app.vue
2
3<script setup>
4const colors = [
5 "red",
6 "green",
7 "blue",
8 "yellow",
9 "purple",
10 "orange",
11 "pink",
12 "brown",
13];
14
15const color = useState(
16 "color",
17 () => colors[Math.floor(Math.random() * colors.length)]
18);
19
20const auth = useStrapiAuth();
21const user = useStrapiUser();
22const credentials = reactive({
23 username: "",
24 email: "",
25 password: "",
26});
27
28async function handleSignUp() {
29 await auth.register(credentials).catch((err) => alert(err.error?.message));
30}
31
32async function handleLogin() {
33 await auth
34 .login({
35 identifier: credentials.username,
36 password: credentials.password,
37 })
38 .catch((err) => alert(err.error?.message));
39}
40</script>
41
42<template>
43 <h1 :style="{ color }">Hello Strapi!</h1>
44 <div v-if="user">
45 <pre>{{ user }}</pre>
46 <button @click="auth.logout">Logout</button>
47 </div>
48 <div v-else>
49 <form @submit.prevent="handleSignUp">
50 <h2>Signup</h2>
51 <input
52 v-model="credentials.username"
53 type="text"
54 placeholder="Username"
55 required
56 />
57 <input
58 v-model="credentials.email"
59 type="email"
60 placeholder="Email"
61 required
62 />
63 <input
64 v-model="credentials.password"
65 type="password"
66 placeholder="Password"
67 required
68 />
69 <button type="submit">Signup</button>
70 </form>
71 <form @submit.prevent="handleLogin">
72 <h2>Login</h2>
73 <input
74 v-model="credentials.username"
75 type="text"
76 placeholder="Username or Email"
77 required
78 />
79 <input
80 v-model="credentials.password"
81 type="password"
82 placeholder="Password"
83 required
84 />
85 <button type="submit">Login</button>
86 </form>
87 </div>
88</template>
89
90<style>
91h1 {
92 font-family: "Pacifico", sans-serif;
93}
94</style>
95
You can also find the complete code in this GitHub repo.
Nitro: The Edge-Agnostic Engine
If you're not a Vue fan, no problem. Nuxt’s core server and build engine is called Nitro.
Nitro is a framework-agnostic engine responsible for all the route handlers, build presets, and minification of outputs.
Used in:
- TanStack Start (React)
- AnalogJS (Angular)
- SolidStart (SolidJS)
- Nuxt (VueJS)
You can even create your own web framework using Nitro.
Summary
Nuxt’s Edge compatibility, powered by its framework-agnostic Nitro engine, gives you the ability to:
- Optimize performance with cold start times near zero
- Serve content close to users across the globe
- Leverage flexible features like modular architecture, pre-rendering, and automatic code-splitting
- Integrate with modern tools like Strapi, Google Fonts, and custom UI libraries—without compromising speed or privacy
If you’re deploying a full-stack application, connecting to a headless CMS, or simply experimenting with SSR, Nuxt makes edge deployment seamless and powerful.
Try building and deploying with a preset like Cloudflare, and join the growing ecosystem of developers building for the edge.
Learn more about Nuxt through its documentation at nuxt.com. You can also explore more Nuxt repositories on github.com/nuxt.
Theodore is a Technical Writer and a full-stack software developer. He loves writing technical articles, building solutions, and sharing his expertise.