Authentication is one of the most critical features in any modern web application. Beyond simple login and registration, many apps require role-based access control (RBAC) — for example, only allowing administrators to access certain pages while restricting standard users.
In this tutorial, we’ll learn how to build Role-Based Authentication in Nuxt 4 with Supabase. Supabase provides a complete backend-as-a-service with authentication, PostgreSQL database, and Row Level Security (RLS), making it an excellent fit for secure and scalable applications.
By the end of this tutorial, you’ll have a working Nuxt 4 app with:
-
User authentication (register, login, logout).
-
Role-based access control (e.g.,
admin
vsuser
). -
Protected routes in Nuxt 4 using middleware.
-
Supabase integration for managing roles and profiles.
This setup can serve as a foundation for building SaaS platforms, dashboards, or any application requiring multiple user roles.
Prerequisites
Before getting started, make sure you have the following:
-
Node.js 20 or later is installed on your machine.
-
Basic knowledge of Vue 3 / Nuxt (components, pages, and routing).
-
A Supabase account (free tier is sufficient).
-
Familiarity with JavaScript or TypeScript.
Optional but recommended:
-
A package manager like npm or pnpm.
-
Git is installed for version control.
Once these prerequisites are ready, we can move on to setting up our Supabase project. 🚀
Setting Up Supabase
In this section, we’ll set up Supabase as our backend for authentication and user roles. Supabase gives us a PostgreSQL database, authentication system, and APIs out of the box — all managed through its dashboard.
1. Create a Supabase Project
-
Go to the Supabase Dashboard and log in with your GitHub or email account.
-
Click New Project.
-
Enter your project name, select the free plan, and choose the nearest region.
-
Supabase will provision your project — this may take a few moments.
Once ready, you’ll see your new project dashboard.
2. Enable Authentication
-
In the left sidebar, go to Authentication → Providers.
-
Under Email, make sure Enable Email/Password Auth is turned on.
-
(Optional) You can also enable third-party providers like Google or GitHub if you want social login.
For this tutorial, we’ll focus on email/password authentication.
3. Create a Profiles Table
By default, Supabase manages users in its auth.users
table. To extend user information (such as assigning roles), we’ll create a custom profiles
table that references each user.
Open the SQL Editor in your Supabase dashboard and run the following script:
create table profiles (
id uuid references auth.users not null primary key,
role text check (role in ('admin', 'user')) default 'user',
created_at timestamp default now()
);
This does the following:
-
id
: References theauth.users
table (same as user ID). -
role
: Can only be"admin"
or"user"
, defaults to"user"
. -
created_at
: Tracks when the profile was created.
4. Insert Sample Data
To test our setup, let’s insert some initial users with different roles.
Since Supabase automatically creates a profile entry after a user signs up, we’ll manually add one for testing purposes:
insert into profiles (id, role) values
('00000000-0000-0000-0000-000000000001', 'admin'),
('00000000-0000-0000-0000-000000000002', 'user');
⚠️ Replace the IDs with actual user IDs from the auth.users
table after you register them.
5. Configure Row Level Security (Optional but Recommended)
By default, Supabase enforces Row Level Security (RLS). To ensure that only authenticated users can access their own profiles, enable policies on the profiles
table.
Run this policy for authenticated users:
alter table profiles enable row level security;
create policy "Allow individual access to own profile"
on profiles for select
using (auth.uid() = id);
This ensures that users can only read their own profile, not others.
✅ At this point, Supabase is fully configured with:
-
Email/password authentication.
-
A
profiles
table with user roles. -
(Optional) RLS for security.
Next, we’ll set up our Nuxt 4 project and connect it with Supabase. 🚀
Creating a New Nuxt 4 Project
With Supabase ready, it’s time to set up our Nuxt 4 frontend application. Nuxt 4 provides a modern developer experience with built-in Vue 3, server-side rendering, and TypeScript support — making it ideal for building secure applications.
1. Initialize a New Nuxt 4 App
Run the following command in your terminal:
npx nuxi@latest init nuxt-supabase-auth
cd nuxt-supabase-auth
This will:
-
Create a new project called
nuxt-supabase-auth
. -
Install Nuxt 4 dependencies.
You can check that everything is working by running:
npm run dev
Then visit http://localhost:3000 — you should see the default Nuxt welcome page. 🎉
2. Install Supabase Client
Next, we need the official Supabase JavaScript client to connect our Nuxt app with Supabase:
npm install @supabase/supabase-js
3. Setup Environment Variables
Nuxt 4 uses runtime config for sensitive values like API keys. Create a .env
file in the project root:
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_KEY=your-anon-public-key
You can find these values in the Supabase dashboard under Project Settings → API.
-
SUPABASE_URL = your project’s unique URL.
-
SUPABASE_KEY = the anon public key (safe to use in the frontend).
⚠️ Never expose your service role key in the frontend! Always use the anon key for client-side operations.
4. Configure Runtime Config
Open nuxt.config.ts
and add the runtime configuration:
export default defineNuxtConfig({
runtimeConfig: {
public: {
supabaseUrl: process.env.SUPABASE_URL,
supabaseKey: process.env.SUPABASE_KEY,
}
}
})
This makes SUPABASE_URL
and SUPABASE_KEY
available to your Nuxt app securely.
✅ At this stage, we have:
-
A fresh Nuxt 4 project.
-
Supabase client installed.
-
Environment variables configured.
Configuring Supabase in Nuxt 4
Now that we have Supabase installed and environment variables configured, let’s integrate it into our Nuxt 4 app. We’ll use a Nuxt plugin so that Supabase is available everywhere (pages, components, and composables).
1. Create a Supabase Plugin
Inside your project, create a new file:
plugins/supabase.client.ts
import { createClient, SupabaseClient } from '@supabase/supabase-js'
import { defineNuxtPlugin, useRuntimeConfig } from 'nuxt/app'
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
// Create Supabase client using runtime config
const supabase: SupabaseClient = createClient(
config.public.supabaseUrl as string,
config.public.supabaseKey as string
)
return {
provide: {
supabase
}
}
})
This plugin:
-
Reads the Supabase URL and Key from runtime config.
-
Initializes a Supabase client.
-
Injects it into the Nuxt app so we can call it using
const { $supabase } = useNuxtApp()
.
2. Using Supabase Anywhere
With the plugin in place, we can now access Supabase in any page, component, or composable. For example:
<script setup lang="ts">
const { $supabase } = useNuxtApp()
// Fetch some data from profiles table
const { data, error } = await $supabase
.from('profiles')
.select('*')
if (error) console.error(error)
</script>
3. Handling Authentication State
Nuxt 4 doesn’t include a built-in authentication store, but we can easily manage the Supabase user with a composable. Let’s create one.
Create a new file:
composables/useAuth.ts
import { useNuxtApp, useState } from "nuxt/app"
export const useAuth = () => {
const user = useState('user', () => null)
const { $supabase } = useNuxtApp() as unknown as { $supabase: any }
const getUser = async () => {
const { data, error } = await $supabase.auth.getUser()
if (!error) {
user.value = data.user
}
}
return { user, getUser }
}
This allows us to:
-
Store the authenticated user globally in
useState
. -
Call
getUser()
to refresh the user state.
Later, we’ll extend this composable to include login, register, and logout.
✅ At this point:
-
Supabase is fully integrated into Nuxt 4.
-
We can fetch data and check the user state.
Next, we’ll implement Authentication (Register & Login pages) to allow users to sign up and log in to our app. 🔐
Authentication (Register & Login)
With Supabase configured in Nuxt 4, the next step is implementing authentication. We’ll create two pages:
-
pages/register.vue
→ For new users to sign up. -
pages/login.vue
→ For existing users to log in.
We’ll also use the useAuth
composable (that you just created) to store the authenticated user state globally.
1. Create a Registration Page
Create a new file:
📄 pages/register.vue
<template>
<div class="max-w-md mx-auto p-6 bg-white rounded-lg shadow">
<h2 class="text-2xl font-bold mb-4">Register</h2>
<form @submit.prevent="handleRegister" class="space-y-4">
<input
v-model="email"
type="email"
placeholder="Email"
class="w-full border p-2 rounded"
required
/>
<input
v-model="password"
type="password"
placeholder="Password"
class="w-full border p-2 rounded"
required
/>
<button
type="submit"
class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700"
>
Register
</button>
</form>
<p v-if="error" class="text-red-500 mt-3">{{ error }}</p>
<p v-if="success" class="text-green-600 mt-3">Registration successful! 🎉</p>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { useNuxtApp } from "nuxt/app"
const { $supabase } = useNuxtApp() as unknown as { $supabase: any }
const email = ref("")
const password = ref("")
const error = ref("")
const success = ref(false)
const handleRegister = async () => {
error.value = ""
success.value = false
const { data, error: signUpError } = await $supabase.auth.signUp({
email: email.value,
password: password.value,
})
if (signUpError) {
error.value = signUpError.message
} else {
success.value = true
}
}
</script>
👉 This allows new users to register with an email and password. Supabase automatically handles email confirmation if enabled in your project settings.
2. Create a Login Page
Create another file:
📄 pages/login.vue
<template>
<div class="max-w-md mx-auto p-6 bg-white rounded-lg shadow">
<h2 class="text-2xl font-bold mb-4">Login</h2>
<form @submit.prevent="handleLogin" class="space-y-4">
<input
v-model="email"
type="email"
placeholder="Email"
class="w-full border p-2 rounded"
required
/>
<input
v-model="password"
type="password"
placeholder="Password"
class="w-full border p-2 rounded"
required
/>
<button
type="submit"
class="w-full bg-green-600 text-white py-2 rounded hover:bg-green-700"
>
Login
</button>
</form>
<p v-if="error" class="text-red-500 mt-3">{{ error }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { useNuxtApp } from "nuxt/app"
import { useAuth } from "../../composables/useAuth"
const { $supabase } = useNuxtApp() as unknown as { $supabase: any }
const { user, getUser } = useAuth()
const email = ref("")
const password = ref("")
const error = ref("")
const handleLogin = async () => {
error.value = ""
const { data, error: signInError } = await $supabase.auth.signInWithPassword({
email: email.value,
password: password.value,
})
if (signInError) {
error.value = signInError.message
} else {
await getUser()
}
}
</script>
👉 This allows users to log in and update the global user
state via useAuth()
.
3. Testing Authentication
-
Run your Nuxt app:
npm run dev
-
Navigate to
/register
, create a new account. -
Go to
/login
, log in with the same credentials. -
After logging in, check the user state by opening the console and typing:
useAuth().user.value
You should see the authenticated user’s info. 🎉
✅ At this point, you have basic Register & Login authentication working with Supabase in Nuxt 4.
Role Management in Supabase
Authentication ensures that only registered users can log in, but many apps need more than that — different roles with different permissions. For example:
-
Admin → full access, manage users and content.
-
Editor → can create and update content but not manage users.
-
User → standard access, limited to their own data.
Supabase doesn’t provide roles by default, but we can easily add them using the public.profiles
table (or any custom table you define).
1. Create a profiles
Table
Go to your Supabase Dashboard, open your project, then navigate to Table Editor → New Table.
Name the table: profiles
Columns to add:
Column | Type | Default/Note |
---|---|---|
id |
UUID | uuid_generate_v4() – Primary Key, match auth.users.id |
email |
Text | Store user’s email |
role |
Text | Default: "user" |
created_at |
Timestamptz | now() |
⚡ Important: Ensure the id
column has a foreign key referencing auth.users.id
.
This way, each authenticated user automatically has a profile entry.
2. Insert Role on Registration
When a new user registers, we’ll insert their profile into the profiles
table with the default role (user
).
Update pages/register.vue
→ inside handleRegister
:
const handleRegister = async () => {
error.value = ""
success.value = false
const { data, error: signUpError } = await $supabase.auth.signUp({
email: email.value,
password: password.value,
})
if (signUpError) {
error.value = signUpError.message
} else {
// Insert user profile with default role = 'user'
if (data.user) {
await $supabase.from("profiles").insert([
{
id: data.user.id,
email: data.user.email,
role: "user",
},
])
}
success.value = true
}
}
3. Fetch User Role After Login
Update your useAuth.ts
composable to fetch the user’s role after authentication:
📄 composables/useAuth.ts
import { useNuxtApp, useState } from "nuxt/app"
export const useAuth = () => {
const user = useState<any>("user", () => null)
const role = useState<string>("role", () => "guest")
const { $supabase } = useNuxtApp() as unknown as { $supabase: any }
const getUser = async () => {
const { data, error } = await $supabase.auth.getUser()
if (!error && data.user) {
user.value = data.user
// Fetch role from profiles
const { data: profile } = await $supabase
.from("profiles")
.select("role")
.eq("id", data.user.id)
.single()
if (profile) {
role.value = profile.role
}
}
}
return { user, role, getUser }
}
Now, every time a user logs in, their role
is stored in the state alongside the user
.
4. Testing Roles
-
Register a new user at
/register
.-
They’ll automatically get the
user
role.
-
-
Go to Supabase Dashboard → Table Editor → profiles.
-
Manually change the role to
"admin"
or"editor"
for testing.
-
-
Log in with that account.
-
Run in the console:
useAuth().role.value
You should see the correct role assigned. 🎉
-
✅ At this point, we have a working role-based authentication system:
-
Users register → profile created with default role.
-
On login → role fetched from
profiles
.
Protecting Routes and Pages by Role
Now that we can authenticate users and assign them roles in Supabase, we need to restrict access to certain pages based on those roles.
Nuxt 4 gives us several ways to handle this:
-
Route middleware → Runs before a page is rendered.
-
Composables → Encapsulate logic for checking roles.
-
Conditional rendering → Hide/show UI elements based on roles.
We’ll use middleware for strong route protection.
1. Create an Auth Middleware
Create a file:
📄 middleware/auth.global.ts
import { defineNuxtRouteMiddleware, navigateTo } from "nuxt/app"
import { useAuth } from "../composables/useAuth"
export default defineNuxtRouteMiddleware((to) => {
const { user } = useAuth()
// Redirect to login if not authenticated
if (!user.value && to.path !== "/login" && to.path !== "/register") {
return navigateTo("/login")
}
})
👉 This ensures only authenticated users can access private routes.
Unauthenticated visitors are redirected to /login
.
2. Create a Role-Based Middleware
Next, let’s restrict pages to admins only.
📄 middleware/admin.ts
import { defineNuxtRouteMiddleware, navigateTo } from "nuxt/app"
import { useAuth } from "../composables/useAuth"
export default defineNuxtRouteMiddleware(() => {
const { role } = useAuth()
if (role.value !== "admin") {
return navigateTo("/") // Redirect non-admins to home
}
})
👉 You can create similar middleware for editor.ts
, user.ts
, etc.
3. Apply Middleware to Pages
Now, let’s protect specific pages using middleware.
📄 pages/admin.vue
<template>
<div class="max-w-2xl mx-auto p-6">
<h1 class="text-3xl font-bold mb-4">Admin Dashboard</h1>
<p>Welcome, only admins can see this page.</p>
</div>
</template>
<script setup lang="ts">
// Protect this page with admin middleware
definePageMeta({
middleware: ["auth", "admin"],
})
</script>
📄 pages/editor.vue
<template>
<div class="max-w-2xl mx-auto p-6">
<h1 class="text-3xl font-bold mb-4">Editor Dashboard</h1>
<p>Welcome, editors can see this page.</p>
</div>
</template>
<script setup lang="ts">
definePageMeta({
middleware: ["auth"], // Require login
})
</script>
👉 Here:
-
admin.vue
→ Requires both authentication and theadmin
role. -
editor.vue
→ Requires only authentication (we could add aneditor.ts
middleware later).
4. Conditional UI Rendering by Role
Sometimes you don’t want to block entire routes, just hide or show features based on roles.
Example: in a Navbar.vue
component:
<template>
<nav class="flex gap-4 p-4 bg-gray-100">
<NuxtLink to="/">Home</NuxtLink>
<NuxtLink v-if="role === 'admin'" to="/admin">Admin</NuxtLink>
<NuxtLink v-if="role === 'editor'" to="/editor">Editor</NuxtLink>
<NuxtLink to="/profile">Profile</NuxtLink>
</nav>
</template>
<script setup lang="ts">
import { useAuth } from "~/composables/useAuth"
const { role } = useAuth()
</script>
👉 This ensures only users with the right role see links to restricted pages.
✅ At this stage:
-
Unauthorized users can’t access protected pages.
-
Role-based middleware ensures only the right users see the right content.
-
UI is adjusted dynamically depending on the user’s role.
Persisting Sessions and Handling Logout
Supabase manages sessions internally with tokens, but we must sync them with our Nuxt app’s state (useAuth
) so that roles and authentication remain available after refresh.
1. Persist Session on Refresh
We’ll extend our useAuth
composable so that it checks for an existing Supabase session when the app loads.
📄 composables/useAuth.ts
import { useNuxtApp, useState } from "nuxt/app"
export const useAuth = () => {
const user = useState<any>("user", () => null)
const role = useState<string>("role", () => "guest")
const { $supabase } = useNuxtApp() as unknown as { $supabase: any }
const getUser = async () => {
const { data, error } = await $supabase.auth.getUser()
if (!error && data.user) {
user.value = data.user
// Fetch role from profiles table
const { data: profile } = await $supabase
.from("profiles")
.select("role")
.eq("id", data.user.id)
.single()
if (profile) {
role.value = profile.role
}
}
}
// 🔥 New: check for active session on app start
const initAuth = async () => {
const {
data: { session },
} = await $supabase.auth.getSession()
if (session?.user) {
user.value = session.user
const { data: profile } = await $supabase
.from("profiles")
.select("role")
.eq("id", session.user.id)
.single()
if (profile) {
role.value = profile.role
}
}
}
const logout = async () => {
await $supabase.auth.signOut()
user.value = null
role.value = "guest"
}
return { user, role, getUser, initAuth, logout }
}
2. Call initAuth
on App Start
We need to initialize auth state when the app loads.
In Nuxt, the best place is a plugin.
📄 plugins/initAuth.client.ts
import { defineNuxtPlugin } from "nuxt/app"
import { useAuth } from "../composables/useAuth"
export default defineNuxtPlugin(async () => {
const { initAuth } = useAuth()
await initAuth()
})
👉 This ensures that whenever the user refreshes, we restore their session and role automatically.
3. Add a Logout Button
Let’s add a logout button in your navbar so users can log out.
📄 components/Navbar.vue
<template>
<nav class="flex gap-4 p-4 bg-gray-100">
<NuxtLink to="/">Home</NuxtLink>
<NuxtLink v-if="role === 'admin'" to="/admin">Admin</NuxtLink>
<NuxtLink v-if="role === 'editor'" to="/editor">Editor</NuxtLink>
<NuxtLink to="/profile">Profile</NuxtLink>
<button
v-if="user"
@click="handleLogout"
class="ml-auto bg-red-600 text-white px-3 py-1 rounded"
>
Logout
</button>
<NuxtLink v-else to="/login" class="ml-auto">Login</NuxtLink>
</nav>
</template>
<script setup lang="ts">
import { useAuth } from "../../composables/useAuth"
const { user, role, logout } = useAuth()
const handleLogout = async () => {
await logout()
}
</script>
4. Test Session Persistence and Logout
-
Log in with a user.
-
Refresh the page → You should still be logged in, with the role correctly loaded.
-
Click Logout → You should be redirected to guest mode (try refreshing again — still logged out ✅).
✅ With this, we now have:
-
Session persistence → Users don’t lose login state on refresh.
-
Logout handling → Securely signs out from Supabase and clears local state.
Restricting API Routes by Role
Nuxt 4’s server routes (in /server/api
) run on the server side and can interact with Supabase or other services.
To enforce role-based access, we’ll:
-
Get the current user session from Supabase.
-
Check their role in the
profiles
table. -
Allow or block access depending on their role.
1. Example: Public API (open to everyone)
📄 server/api/public.get.ts
export default defineEventHandler(() => {
return { message: "This is a public endpoint, accessible by anyone." }
})
👉 No checks needed, all users (even unauthenticated) can access.
2. Example: Protected API (only logged-in users)
📄 server/api/protected.get.ts
import { serverSupabaseUser } from "#supabase/server"
export default defineEventHandler(async (event) => {
const user = await serverSupabaseUser(event)
if (!user) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" })
}
return { message: `Hello ${user.email}, you are logged in!` }
})
👉 Here, only logged-in users can access this route.
3. Example: Admin-Only API
📄 server/api/admin.get.ts
import { serverSupabaseClient, serverSupabaseUser } from "#supabase/server"
export default defineEventHandler(async (event) => {
const user = await serverSupabaseUser(event)
const client = await serverSupabaseClient(event)
if (!user) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" })
}
// Fetch user role from profiles
const { data: profile } = await client
.from("profiles")
.select("role")
.eq("id", user.id)
.single()
if (!profile || profile.role !== "admin") {
throw createError({ statusCode: 403, statusMessage: "Forbidden: Admins only" })
}
return { message: "Welcome, Admin! 🎉 You can access this API." }
})
👉 Here, even if a logged-in user tries to call /api/admin
, they’ll get 403 Forbidden
unless they’re an admin.
4. Calling These APIs from the Frontend
From a Vue component (e.g., pages/profile.vue
), you can call these APIs with $fetch
:
<script setup lang="ts">
import { ref } from "vue"
const publicMessage = ref("")
const protectedMessage = ref("")
const adminMessage = ref("")
const error = ref("")
const loadData = async () => {
try {
publicMessage.value = await $fetch("/api/public").then(r => r.message)
protectedMessage.value = await $fetch("/api/protected").then(r => r.message)
adminMessage.value = await $fetch("/api/admin").then(r => r.message)
} catch (err: any) {
error.value = err?.data?.statusMessage || "Error fetching API"
}
}
onMounted(loadData)
</script>
<template>
<div class="p-6 space-y-3">
<h1 class="text-2xl font-bold">Profile API Test</h1>
<p><strong>Public:</strong> {{ publicMessage }}</p>
<p><strong>Protected:</strong> {{ protectedMessage }}</p>
<p><strong>Admin:</strong> {{ adminMessage }}</p>
<p v-if="error" class="text-red-600">Error: {{ error }}</p>
</div>
</template>
👉 When logged in as a normal user, /api/admin
should return 403 Forbidden
. When logged in as an admin, it should return the success message.
✅ At this stage, your app has end-to-end role-based protection:
-
Pages → restricted by middleware.
-
UI → role-aware navigation.
-
APIs → fully secured with Supabase session + role checks.
Conclusion + Best Practices
In this tutorial, we successfully built Role-Based Authentication in Nuxt 4 with Supabase. We started by setting up Supabase as the backend for authentication and role management, integrated it into a Nuxt 4 project, created reusable authentication composables, implemented login and registration, and enforced role-based access control at the page level. Finally, we persisted sessions and added logout functionality to complete the authentication flow.
This approach provides a scalable, secure, and modern authentication system that can be extended to handle more complex use cases like multi-role support, granular permissions, or team-based access control.
🔑 Key Takeaways
-
Supabase Integration: Supabase makes it easy to manage authentication and role data without building a custom backend.
-
Nuxt Composables: Reusable hooks like
useAuth
help centralize authentication logic and simplify state management. -
Role-Based Middleware: Protecting routes with middleware ensures that only authorized users can access restricted pages.
-
Session Persistence: Handling sessions via
onAuthStateChange
ensures smooth navigation across refreshes or tab changes.
✅ Best Practices for Role-Based Authentication
-
Use Row-Level Security (RLS): Supabase supports PostgreSQL RLS policies—use them to enforce role-based restrictions at the database level for extra security.
-
Centralize Role Logic: Avoid duplicating role checks across components. Instead, rely on middleware and composables.
-
Handle Token Refresh: Ensure that refresh tokens are managed properly to prevent session expiration issues.
-
Secure API Routes: If you expose server routes (via Nuxt server routes or APIs), always validate JWT tokens on the server, not just the client.
-
Audit & Logging: Keep track of authentication and authorization events for debugging and security audits.
By following these best practices, your Nuxt 4 + Supabase app will remain secure, scalable, and maintainable, whether you’re building a SaaS product, an internal dashboard, or a role-sensitive application.
🚀 Now you’re ready to expand this project further—add user profiles, implement advanced permissions, or integrate with external APIs—while keeping your role-based authentication system strong and flexible.
You can find the full source code on our GitHub.
That's just the basics. If you need more deep learning about Nuxt.js, you can take the following cheap course:
- Nuxt 3 & Supabase Mastery: Build 2 Full-Stack Apps
- Build Web Apps with Nuxt.js 3: Master TypeScript & API [New]
- The Nuxt 3 Bootcamp - The Complete Developer Guide
- Complete Nuxt.js Course (EXTRA React)
- The Complete NUXT 3 Bootcamp: Full-Stack Guide
- Nuxt 3 Authentication with Laravel Sanctum:A Practical Guide
- Learn How to Make Your Nuxt 3 App SEO-Friendly
- Headless Prestashop with Nuxt JS
Thanks!