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.
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.
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.
Here are some of the benefits of the Edge.
The Edge also has its limitations which include:
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:
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?
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:
Some features found in Next.js are also available in Nuxt.js. They include the following:
1export default defineNuxtConfig({
2 routeRules: {
3 '/dashboard/**': { ssr: false }
4 }
5})
Nuxt also comes with its own official modules so as to keep it as light as possible. These include the following:
You can install any of the modules above using the command below:
npx nuxt module add <module>
There is the Strapi module for Nuxt. You can install it using the command below:
npm nuxt module add strapi
The next step is to set the Strapi URL in the environment variable:
STRAPI_URL=http://localahost:1337
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.
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:
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:
1nuxt build --preset-cloudflare_module
1nuxt build --preset-deno_deploy
1nuxt build --preset-netlify_edge
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}
Let's demonstrate Server-side rendering with Nuxt.js.
Say you create a basic Nuxt.js application that contains:
app.vue
)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.
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.
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.
@nuxt/fonts
ModuleLet'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.
Now, let's demonstrate how to integrate Strapi authentication using the nuxtjs/strapi module. Let's leverage Strapi authentication!
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
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
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:
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.
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:
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.
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 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:
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.
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:
You can even create your own web framework using Nitro.
Nuxt’s Edge compatibility, powered by its framework-agnostic Nitro engine, gives you the ability to:
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.