--- title: 'Build a User Management App with Nuxt 3' description: 'Learn how to use Supabase in your Nuxt 3 App.' --- <$Partial path="uiLibCta.mdx" /> <$Partial path="quickstart_intro.mdx" /> ![Supabase User Management example](/docs/img/user-management-demo.png) If you get stuck while working through this guide, you can find the [full example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nuxt3-user-management). <$Partial path="project_setup.mdx" variables={{ "framework": "nuxt", "tab": "frameworks" }} /> ## Building the app Build the Vue 3 app from scratch. ### Initialize a Nuxt 3 app We can use [`nuxi init`](https://nuxt.com/docs/getting-started/installation) to create an app called `nuxt-user-management`: ```bash npx nuxi init nuxt-user-management cd nuxt-user-management ``` Then install the only additional dependency: [Nuxt Supabase](https://supabase.nuxtjs.org/). We only need to import Nuxt Supabase as a dev dependency. ```bash npm install @nuxtjs/supabase --save-dev ``` And finally we want to save the environment variables in a `.env`. All we need are the API URL and the key that you copied [earlier](#get-api-details). <$CodeTabs> ```bash name=.env SUPABASE_URL="YOUR_SUPABASE_URL" SUPABASE_KEY="YOUR_SUPABASE_PUBLISHABLE_KEY" ``` These variables will be exposed on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database. Amazing thing about [Nuxt Supabase](https://supabase.nuxtjs.org/) is that setting environment variables is all we need to do in order to start using Supabase. No need to initialize Supabase. The library will take care of it automatically. ### App styling (optional) An optional step is to update the CSS file `assets/main.css` to make the app look better. You can find the full contents of this file [in the example repository](https://github.com/supabase-community/nuxt3-quickstarter/blob/main/assets/main.css). <$CodeTabs> ```typescript name=nuxt.config.ts import { defineNuxtConfig } from 'nuxt' // https://v3.nuxtjs.org/api/configuration/nuxt.config export default defineNuxtConfig({ modules: ['@nuxtjs/supabase'], css: ['@/assets/main.css'], }) ``` ### Set up Auth component Set up a Vue component to manage sign-ins and sign-ups. We'll use Magic Links, so users can sign in with their email without using passwords. <$CodeTabs> ```vue name=/components/Auth.vue ``` ### User state To access the user information, use the composable [`useSupabaseUser`](https://supabase.nuxtjs.org/composables/usesupabaseuser) provided by the Supabase Nuxt module. ### Account component After a user is signed in we can allow them to edit their profile details and manage their account. Create a new component called `Account.vue`. <$CodeTabs> ```vue name=components/Account.vue ``` ## Profile photos Next, add a way for users to upload a profile photo. Supabase configures every project with [Storage](/docs/guides/storage) for managing large files like photos and videos. ### Create an upload widget Start by creating a new component: <$CodeTabs> ```vue name=components/Avatar.vue ``` ### Launch! With all the components in place, update `app.vue`: <$CodeTabs> ```vue name=app.vue ``` Once that's done, run this in a terminal window: ```bash npm run dev ``` And then open the browser to [localhost:3000](http://localhost:3000) and you should see the completed app. ![Supabase Nuxt 3](/docs/img/supabase-vue-3-demo.png) At this stage you have a fully functional application! ## Add a server route So far the app authenticates the user on the client. For protected API endpoints or server-rendered data, you need a server route that verifies the session. [`@supabase/server`](https://supabase.github.io/server/) handles the full flow through a single middleware: it validates the JWT locally (using your project's asymmetric signing keys, no round-trip to the Auth server), attaches an RLS-scoped Supabase client and the user's claims to the request, and rejects unauthenticated requests with a 401 before your handler runs. ```bash npm install @supabase/server ``` <$CodeTabs> ```typescript name=server/api/profile.get.ts import { withSupabase } from '@supabase/server/adapters/h3' import { defineHandler } from 'h3' export default defineHandler({ middleware: [withSupabase({ auth: 'user' })], handler: async (event) => { const { supabase, userClaims } = event.context.supabaseContext const { data, error } = await supabase .from('profiles') .select('username, website, avatar_url') .eq('id', userClaims.id) .single() if (error) { throw createError({ statusCode: 500, statusMessage: error.message }) } return data }, }) ``` For an unauthenticated route, pass `auth: 'none'`. For app-wide auth, register `withSupabase({ auth: 'user' })` as a Nuxt server middleware at `server/middleware/supabase.ts` instead. See the [h3/Nuxt adapter docs](https://supabase.github.io/server/adapters/h3) for typing, route overrides, and the full API.