Setting up tailwindcss v4 in a Monorepo with Nuxt

With the latest changes in TailwindCSS v4, setting up Tailwind and having a shared config across your apps in a monorepo can be challenging. In this article, I’ll walk you through how to set up Tailwind and create a shared config that keeps your styles clean, consistent, and DRY across your Nuxt apps.
Let’s roll up our sleeves and get into it!

Tools

Before we start, there are some tools you'll need:

  • pnpm - a new generation of package management tools that also works for monorepos

  • Tailwindcss

  • Nuxt

  • Vscode(or any preferred IDE)

Setting up a monorepo

Install pnpm

npm install -g pnpm # install pnpm globally

Create a new directory and initialize the package manager with pnpm

mkdir tailwind-v4-nuxt-monorepo

cd tailwind-v4-nuxt-monorepo 

pnpm init

Cloning the repo? Just swap out mkdir tailwind-v4-nuxt-monorepo for git clone <repository-link> and you’re good to go.

Once you’ve finished setting up your directory, go ahead and open it in your favorite code editor. If you're using VS Code, you can simply run:

code .

Set up the workspace

To set up the workspace, you have to create pnpm-workspace.yaml in your project root directory — It’s what tells pnpm which folders to treat as workspaces. For more context, read it up here.

touch pnpm-workspace.yaml # creates a new file

Add these configurations to the pnpm-workspace.yaml

packages:
  - 'apps/*'
  - 'packages/*'

This tells pnpm that your workspaces include all directories in the apps and packages folders.

The apps folder is basically where your application sits, while the packages folder is where your shared packages are placed

Create your workspace folders

Since the apps folder will house our applications, let’s go ahead and create it, then spin up our first nuxt app inside

mkdir apps && cd apps # creates a new apps folder and navigates to the folder

Now let’s install nuxt into the apps folders

pnpm create nuxt web-1 # replace web-1 with your app. name

Congratulations🥳. Your Nuxt app has been installed successfully.

Now, let's create our shared tailwind-config folder in the packages folder (remember, the packages folder is where your shared packages are placed).

Navigate back to your root folder to create the packages folder.

mkdir packages && cd packages # creates a new packages folder and navigates to the folder

Now let’s create the shared folder. We are going to name it “tailwind-config.”

mkdir tailwind-config

cd tailwind-config 

pnpm init # pnpm init would generate package.json file for tailwind-config directory

After running the above command, add "private": true to the generated package.json file. Your package.json file should look like this:

{
  "name": "tailwind-config",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

The purpose of adding "private": true is to ensure that the package is not published to NPM or anywhere else. Instead, it is referenced and used locally within our workspace.

At this point, your file structure should look like this:

├── apps
│   └── web-1
│       ├── README.md
│       ├── app.vue
│       ├── nuxt.config.ts
│       ├── package.json
│       ├── public
│       │   ├── favicon.ico
│       │   └── robots.txt
│       ├── server
│       │   └── tsconfig.json
│       └── tsconfig.json
├── package.json
├── packages
│   └── tailwind-config
│       └── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml

Installing Tailwind and setting the shared configuration

After setting up the Nuxt application and the directory for our tailwind-config, let's install tailwindcss.

Let’s start with installing tailwindcss in our tailwind-config directory and set up the shared configuration

pnpm add --filter tailwind-config tailwindcss

After successfully installing tailwindcss, we will create our index.css file to set our configurations and import tailwindcss. Here's how to do it:

/* packages/tailwind-config/index.css */

@import "tailwindcss";

Note: Because tailwind-config is solely for configurations; you don’t need to install @tailwindcss/vite as stated in the tailwind documentation. Using the import works just fine

After creating the index.css file, change "main": "index.js" to "main": "index.css" in your package.json file, so it sets the entry point when imported(from the apps directory) to the CSS file.

This should be what the modified file should look like

// packages/tailwind-config/package.json
{
  "name": "tailwind-config",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "main": "index.css",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "tailwindcss": "^4.1.7"
  }
}

Integrating the shared tailwind-config from the Nuxt app

Now let’s go over to the apps directory to install tailwindcss and use the configuration we just set up

pnpm add --filter web-1 tailwindcss @tailwindcss/vite

Once Tailwind is installed in your Nuxt app, there are a few tweaks you’ll need to make to get everything working just right:

  1. In the nuxt.config.ts, first add import tailwindcss from "@tailwindcss/vite";.

  2. Next, you add the imported tailwindcss to the vite.plugins in your nuxt.config.ts

     vite: {
         plugins: [
           tailwindcss(),
         ],
       },
    

    So your nuxt.config.ts file should be updated to:

     import tailwindcss from "@tailwindcss/vite";
    
     // https://nuxt.com/docs/api/configuration/nuxt-config
     export default defineNuxtConfig({
       compatibilityDate: '2025-05-15',
       devtools: { enabled: true },
       vite: {
         plugins: [
           tailwindcss(),
         ],
       },
     })
    
  3. Create your assets/css/main.css file and import the shared configuration

     /* /apps/web-1/assets/css/main.css */
    
     @import "tailwind-config"
    

    Remember, you are importing the configurations from packages/tailwind-config/index.css and the file already has the @import “taildwincss", so there is no need to add the @import “taildwincss" again in the apps/web-1/assets/css/main.css file. That’s the beauty of using a shared component

  4. Now go back to the nuxt.config.ts file to add your CSS. Your updated file should now be:

     // apps/web-1/nuxt.config.ts
     import tailwindcss from "@tailwindcss/vite";
     // https://nuxt.com/docs/api/configuration/nuxt-config
     export default defineNuxtConfig({
       compatibilityDate: '2025-05-15',
       devtools: { enabled: true },
       css: ['~/assets/css/main.css'],
       vite: {
         plugins: [
           tailwindcss(),
         ],
       },
     })
    

To use the shared package in the nuxt app, you need to add the shared library. You can do this manually or using pnpm


pnpm add tailwind-config --filter web-1 --workspace

Now your package.json file is updated with the tailwind-config set up and should be something like this

// apps/web-1/package.json
{
  "name": "nuxt-app",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "dependencies": {
    "@tailwindcss/vite": "^4.1.7",
    "nuxt": "^3.17.3",
    "tailwind-config": "workspace:^",
    "tailwindcss": "^4.1.7",
    "vue": "^3.5.13",
    "vue-router": "^4.5.1"
  }
}

Testing shared tailwind configuration

Now that we are done with this setup, let’s add a simple customization to our tailwind and test that it works

Using @theme from tailwind theme variable, let’s add new color variable

/* packages/tailwind-config/index.css */

@import "tailwindcss";

@theme {
  --color-primary: pink;
  --color-secondary: blue;
}

After modifying our tailwind configuration, all we have to do now is use it in the Nuxt app

Start the local server:

pnpm --filter web-1 dev

Result:

🎉 Congrats, we made it to the end!
You’ve just walked through how to set up TailwindCSS in a Nuxt-powered monorepo. High five for that! 🖐️

I know some parts might’ve felt like a whirlwind (pun intended), so if anything’s still a bit fuzzy, feel free to dive deeper into the official docs for clarity:

And yep — don’t forget, we’re using pnpm workspaces to keep everything tidy and connected. Onward to building beautiful, scalable UIs! 💻✨

Github repo - https://github.com/Anumide/tailwind-v4-nuxt-monorepo

2
Subscribe to my newsletter

Read articles from Alaba Samuel Ayomide directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Alaba Samuel Ayomide
Alaba Samuel Ayomide

Passionate front-end developer with a strong zeal for Web development