Guides & Tutorials

Pairing Nuxt 3 with TailwindCSS and Supabase

Guides & Tutorials

Pairing Nuxt 3 with TailwindCSS and Supabase

So Nuxt 3 is out and I'm obviously very excited about it so I went ahead and paired it up with TailwindCSS and Supabase.

Quick recap in case you missed some of the highlights, Nuxt 3 shipped with some shiny new features like:

  • New CLI - nuxi
  • Nuxt Nitro - a new server engine
  • Native TypeScript support
  • Multiple rendering modes (SSR, CSR, SSG)
  • Support for the latest Vue 3 features
  • Bundling with both Vite and Webpack 5

You can learn more about them in the announcement post.

So I paired up Nuxt 3 with TailwindCSS and Supabase to create a newsletter subscription form eh? let me tell you all about it.

Also feel free to build along in this project if you'd like a hands-on experience. Otherwise, the completed project is available on this repo if you'd prefer to mess with the code.

Create a Nuxt 3 project

The Nuxt 3 docs is pretty straightforward on creating new projects. With the new CLI tool nuxi, we can quickly start a new project with the commands below.

Initialize a nuxt3 project

npx nuxi init nuxt3-app 

Change into the new project directory

cd nuxt3-app # change into the project directory

Install the dependencies

yarn install

Lastly, start the dev server with:

yarn dev 

Your project should be running on port 3000 locally. Great. step 1 is done. Let's move on!

Add TailwindCSS to the project

Be mindful that Nuxt 3 is in beta, which means that things are prone to change. The technique to get Tailwind working has may change so the instructions I'm going to explain below is based on what is currently working at the time of writing this post.

Install TailwindCSS via the terminal:

npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

Create a Tailwind config file:

npx tailwindcss init

This command will create a tailwind.config.js file at the root of your project, open the file and update it like so:

// tailwind.config.js
module.exports = {
  mode: "jit",
  purge: [
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  variants: {
    extend: {},
  plugins: [],

Next, we need to include Tailwind in our CSS file. Mine is located at this path: assets/css/tailwind.css. Let's update this file with this snippet:

@tailwind base;
@tailwind components;
@tailwind utilities;

Finally, we need to tell Nuxt how to find that file and also set up our post CSS options to include both the Tailwind and autoprefixer plugins. Open nuxt.config.ts and update it with:

import { defineNuxtConfig } from "nuxt3";

export default defineNuxtConfig({
  css: ["~/assets/css/tailwind.css"],
  build: {
    postcss: {
      postcssOptions: {
        plugins: {
          tailwindcss: {},
          autoprefixer: {},

According to this directive, it is advised to import Tailwind inside the App.vue file as opposed to using the css prop as we did in the snippet above.

So alternatively, you can open your App.vue file, create a <script> tag to import your CSS file like so:

<script lang="ts" setup>
import './assets/css/tailwind.css'

This will give you Hot Module Replacement.

And that's it. We have TailwindCSS installed in our Nuxt 3 project and all you need to do now is add Tailwind classes to your elements and boom, beauty...

Install Supabase

I will assume that you have a Supabase account, if you don't you can create one if you need a simple database solution for your projects.

Adding Supabase to any project is pretty straightforward as you'll see shortly. Just run the command below:

npm install @supabase/supabase-js

And that's it!

Create a newsletter subscription form with Tailwind

So I mentioned earlier that I built a newsletter subscription form with these 3 tools that I've just set up, here's how:

First, in App.vue, update the template to show this Tailwind form:

  <form class="text-gray-600 body-font">
    <div class="container px-5 py-24">
      <div class="lg:w-3/5 md:w-1/2">
        <h1 class="text-gray-900">
          Subscribe to my newsletter
        <p class="leading-relaxed mt-4">
          I send out a weekly newsletter with tips and tricks for web
        class=" lg:w-2/6 md:w-1/2 bg-gray-100"
        <h2 class="text-gray-900 text-lg font-medium title-font mb-5">

        <div class="relative mb-4">
          <label for="email" class="leading-7 text-sm text-gray-600"
          <input type="email" id="email" name="email"
            class="w-full bg-white rounded"
          class="text-white bg-indigo-500"

Note that I removed some of the Tailwind classes on the snippet above to keep it nice and clean.

This form will simply collect the email address of the user who has so graciously decided to sign up for our fake newsletter. It looks like this btw!

screenshot of the newsletter form

Save form data to Supabase

Next, how do we save this email address to our Supabase database? Good question.

The first thing we need to do is get the user's email from our form and then post it to Supabase using our Nuxt 3 API route. Did I say API routes in Nuxt? Yep, we have those now. To do that, let's set up the script in our app.vue file to collect the email and send it off to our endpoint:

<script setup>
  // variable to hold the response from supabase
  const DBResponse = ref([]);
  // variable to hold the provided email address
  const userEmail = ref("");
  // function to send email to our server route
  async function postToDB() {
    const result = await fetch(`/api/subscribe?email=${userEmail.value}`);
    const data = await result.json();
    DBResponse.value = data;

And that's it. What's happening here is, when a user provides their email in our newsletter form, we'll collect the email and send it to our Nuxt API route at this path /server/api/subscribe which we haven't created yet. So, create the file and set it up like so:

import * as url from "url";
import { createClient } from "@supabase/supabase-js";

export default async (req, res) => {
  const queryObject = url.parse(req.url, true).query;
  const { SUPABASE_URL, SUPABASE_KEY } = process.env;
  try {
    // Initialize Supabase
    const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
    let response;
    const { email } = queryObject;
    if (email) {
      // Save user to Supabase database
      const { data, error } = await supabase.from("Subscribers").upsert({
        email: email,
      response = data;
    res.writeHead(200, { "Content-Type": "application/json" });
  } catch (error) {
    res.writeHead(500, { "Content-Type": "application/json" });

Since we appended the provided email to the API route from the client when we did something like: fetch('/api/subscribe?email=${userEmail.value}'), we had to then destructure it out of the queryObject using the url package we imported at the very top.

We have the user's email, great! we can now send it off to our database. Since we are using Supabase for this, I've created a Subscribers table in one of my Supabase projects so I can store these emails there.

I used some environment variables in the snippet above to initialize Supabase. If you need help finding your Supabase credentials, select your project on the Supabase dashboard, click on the API tab on the left side nav and click authentication. This will show you your credentials for the selected project.

And that's it! Time to take this project for a spin

test the app

If you'd like a video tutorial to see me do this live, watch it here on YouTube


Kind reminder that though we are excited about these new features in Nuxt 3, it is still in public beta and should not be used in production yet. That said, I had fun putting this together and I hope it helps you add TailwindCSS and Supabase to your Nuxt 3 projects. Source code is here on Github and demo is live on Netlify

Keep reading

Recent posts

Book cover with the title Deliver web project 10 times faster with Jamstack enterprise

Deliver web projects 10× faster

Get the whitepaper