mastering supabase auth in nuxt.js: a practical guide and industry insights
introduction
welcome! in this practical guide we’ll walk you through mastering supabase auth in nuxt.js. whether you’re a beginner, a student, or a full‑stack engineer, you’ll learn how to integrate secure authentication, manage sessions, and keep your app seo‑friendly—all while keeping your coding workflow smooth and devops ready.
prerequisites
- node.js ≥ 18 and npm or yarn
- basic knowledge of nuxt 3 and vue 3
- a supabase account (free tier works fine)
- understanding of full‑stack concepts and seo basics
step‑by‑step implementation
1. create a supabase project
log in to supabase, start a new project, and copy the supabase_url and supabase_anon_key. these keys will be used in your nuxt app.
2. install the supabase nuxt module
# using npm
npm i @supabase/supabase-js @nuxtjs/supabase
# or with yarn
yarn add @supabase/supabase-js @nuxtjs/supabase
3. configure nuxt to load environment variables
# .env (place at the project root)
nuxt_public_supabase_url=https://your-project.supabase.co
nuxt_public_supabase_anon_key=your-anon-key
4. initialize supabase in nuxt.config.ts
import { definenuxtconfig } from 'nuxt'
export default definenuxtconfig({
modules: ['@nuxtjs/supabase'],
runtimeconfig: {
public: {
supabaseurl: process.env.nuxt_public_supabase_url,
supabaseanonkey: process.env.nuxt_public_supabase_anon_key,
},
},
})
5. build a simple sign‑up form
<template>
<form @submit.prevent="signup">
<input v-model="email" type="email" placeholder="email" required />
<input v-model="password" type="password" placeholder="password" required />
<button type="submit">sign up</button>
</form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { usesupabaseclient } from '#supabase'
const email = ref('')
const password = ref('')
const supabase = usesupabaseclient()
const signup = async () => {
const { error } = await supabase.auth.signup({
email: email.value,
password: password.value,
})
if (error) {
alert(`❌ ${error.message}`)
} else {
alert('✅ check your email for a confirmation link!')
}
}
</script>
6. sign‑in form (similar to sign‑up)
<template>
<form @submit.prevent="signin">
<input v-model="email" type="email" placeholder="email" required />
<input v-model="password" type="password" placeholder="password" required />
<button type="submit">sign in</button>
</form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { usesupabaseclient } from '#supabase'
const email = ref('')
const password = ref('')
const supabase = usesupabaseclient()
const signin = async () => {
const { error, data } = await supabase.auth.signinwithpassword({
email: email.value,
password: password.value,
})
if (error) {
alert(`❌ ${error.message}`)
} else {
// store the session in a cookie for ssr (see next section)
console.log('✅ logged in:', data.session)
}
}
</script>
7. session handling and ssr support
nuxt 3 can run both on the client and the server. to keep the user logged in across page reloads, store the session in a httponly cookie.
// plugins/supabase.server.ts
import { definenuxtplugin } from '#app'
import { createclient } from '@supabase/supabase-js'
export default definenuxtplugin((nuxtapp) => {
const config = useruntimeconfig()
const supabase = createclient(
config.public.supabaseurl,
config.public.supabaseanonkey,
{
auth: {
// persist the session in a cookie
storage: {
getitem: (key) => nuxtapp.ssrcontext?.event?.req?.cookies?.[key] ?? null,
setitem: (key, value) => {
// set cookie on the server response
const event = nuxtapp.ssrcontext?.event
if (event) {
event.res.setheader('set-cookie', `${key}=${value}; httponly; path=/; samesite=lax`)
}
},
removeitem: (key) => {
const event = nuxtapp.ssrcontext?.event
if (event) {
event.res.setheader('set-cookie', `${key}=; max-age=0; path=/; httponly`)
}
},
},
},
}
)
nuxtapp.provide('supabase', supabase)
})
8. protecting routes (middleware)
// middleware/auth.global.ts
export default definenuxtroutemiddleware((to, from) => {
const supabase = usesupabaseclient()
const user = supabase.auth.user()
if (!user && to.path !== '/login') {
return navigateto('/login')
}
})
9. adding seo‑friendly meta tags
when you render pages on the server, search engines can index the content correctly. use nuxt’s usehead composable.
// pages/dashboard.vue
<script setup lang="ts">
import { usehead } from '#app'
import { usesupabaseclient } from '#supabase'
usehead({
title: 'dashboard – my app',
meta: [
{ name: 'description', content: 'secure dashboard for logged‑in users.' },
{ property: 'og:title', content: 'dashboard – my app' },
{ property: 'og:description', content: 'secure dashboard for logged‑in users.' },
],
})
const supabase = usesupabaseclient()
const user = supabase.auth.user()
</script>
10. devops tips for a production‑ready setup
- environment separation: keep
supabase_urlandsupabase_anon_keyin separate.envfiles for dev, staging, and prod. - ci/cd pipelines: use github actions or gitlab ci to run
npm run lintandnpm run testbefore deploying. - monitoring: enable supabase logs and set up alerts for authentication failures.
- performance: leverage nuxt’s static site generation (ssg) for public pages and keep auth‑protected pages server‑rendered.
putting it all together – a minimal project structure
my-nuxt-app/
├─ .env
├─ nuxt.config.ts
├─ plugins/
│ └─ supabase.server.ts
├─ middleware/
│ └─ auth.global.ts
├─ pages/
│ ├─ index.vue ← public landing page (seo‑optimized)
│ ├─ login.vue ← sign‑in / sign‑up forms
│ └─ dashboard.vue ← protected, uses usehead for seo
└─ components/
└─ authform.vue ← reusable form component
conclusion
by following these steps you now have a full‑stack nuxt.js application with robust supabase auth, ready for devops pipelines and seo‑friendly rendering. keep experimenting, add social logins, and remember that clear, modular code is the key to scaling both your project and your knowledge.
Comments
Share your thoughts and join the conversation
Loading comments...
Please log in to share your thoughts and engage with the community.