Nuxt 3 Layers: The Building Blocks of a SaaS Factory - UI Layer
4 weeks ago - 29 min read • #frontend, #nuxt, #tailwindcss, #layersIn the world of software development, modularity and reusability are the keys to scaling efficiently. Nuxt 3 introduces an exciting feature called layers—a powerful way to create modular, reusable applications. Think of layers as the assembly lines in your SaaS factory, where each line is responsible for a specific component of your app, from the UI to business logic.
For example, in my SaaS factory project, I've designed the architecture to include multiple layers: a UI layer, a database layer, and a Stripe layer. This approach lets me reuse these components across projects or even across teams, without duplicating code.
In this blog series, I’ll show you how to use Nuxt 3 layers effectively. This first part will focus on setting up the UI layer with TailwindCSS and referencing it in the main app.
Why Use Layers in Nuxt 3?
Layers allow you to:
- Encapsulate Functionality: Separate UI components, business logic, and utilities into distinct modules.
- Promote Reusability: Share a common UI or utility layer across multiple projects.
- Streamline Collaboration: Teams can focus on specific layers without stepping on each other’s toes.
- Simplify Maintenance: Bug fixes or updates in one layer automatically propagate to all projects using it.
For a SaaS factory, this means you can build once and deploy often.
The Goal: A TailwindCSS-Powered UI Layer
In this article, we’ll create a reusable UI layer for styling your app. It will be powered by TailwindCSS and set up in a way that makes it easy to integrate into your main Nuxt 3 application—or any other Nuxt 3 project.
Step 1: Create PNPM Workspaces
Create a file pnpm-worspace.yaml
in the project root and insert:
// pnpm-workspace.yaml
packages:
- "packages/**"
- "apps/**"
run:
$ pnpm init
Step 2: Create the UI Layer
$ mkdir packages
$ cd packages
$ pnpx nuxi init --template layer ui
Let me explain what these commands do:
mkdir packages
- Creates a new directory named "packages"cd packages
- Changes the current working directory to the newly created "packages" directorypnpx nuxi init --template layer ui
- Initializes a new Nuxt Layer project using a UI template. This creates a base UI layer package that can be shared across multiple Nuxt applications, containing reusable UI components, composables, and utilities.
Step 3: Set the UI layer name
Setting the UI layer name so we can reference it as a NPM package in the main app:
// packages/ui/package.json
{
"name": "@jiprochazka/ui"
}
Step 4: Installing Tailwindcss
$ cd packages/ui
$ pnpx nuxi@latest module add tailwindcss
// packages/ui/nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
experimental: { appManifest: false },
modules: ["@nuxtjs/tailwindcss"]
})
Step 5: Creating the app
Go to the project root and run:
$ mkdir apps
$ cd apps
$ pnpx nuxi@latest init my-app
Here's what these commands do:
mkdir apps
- Creates a new directory named "apps"cd apps
- Changes the current working directory to the newly created "apps" directorypnpx nuxi@latest init my-app
- Initializes a new Nuxt application using the latest version of Nuxt. The app will be created in a directory named "my-app". This command creates a fresh Nuxt project with all the necessary files and dependencies to start building your web application.
Step 6: Referencing the UI layer
In the app's package.json file we must se the UI layer as a dependency:
// apps/my-app/package.json
{
"name": "@jiprochazka/my-app", // set the project name
...
"dependencies": {
"@jiprochazka/ui": "workspace:*", // here must be the exact name from the 'packages/ui/package.json'
...
}
}
In the nuxt.config.ts file the app must extend the UI layer:
// apps/my-app/nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
extends: ["@jiprochazka/ui"]
})
Step 7: Setting PNPM command in the root and installation
For a convenience we can set few pnpm commands in the root package.json:
{
"scripts": {
...
"packages": "pnpm --filter",
"ui": "pnpm packages @jiprochazka/ui",
"my-app": "pnpm packages @jiprochazka/my-app"
}
}
Here's what these scripts in the root package.json do:
"packages": "pnpm --filter"
- A base command that allows running commands for specific workspaces in your monorepo using PNPM's filter feature"ui": "pnpm packages @jiprochazka/ui"
- A shorthand to run commands specifically for your UI layer package. You can use it likepnpm ui dev
orpnpm ui build
"my-app": "pnpm packages @jiprochazka/my-app"
- Similar shorthand for your Nuxt application. Usage:pnpm my-app dev
orpnpm my-app build
These scripts make it easier to run commands for specific parts of your monorepo without having to change directories or use longer filter commands.
Tailwindcss settings
When you define a tailwind.config.js
in your UI layer, it serves as a base configuration with your design system's core styles, theme, and components. However, applications using this layer can extend or override these settings by creating their own tailwind.config.js
. Nuxt 3 automatically merges these configurations, with the main app's settings taking precedence. This allows you to maintain consistent base styling through the UI layer while giving each application the flexibility to customize its appearance by extending or overwriting specific Tailwind classes, colors, spacing, or other design tokens.
Example:
// packages/ul/tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [],
darkMode: "class",
theme: {
extend: {
colors: {
primary: {
50: "#eff6ff",
100: "#dbeafe",
200: "#bfdbfe",
300: "#93c5fd",
400: "#60a5fa",
500: "#3b82f6",
600: "#2563eb",
700: "#1d4ed8",
800: "#1e40af",
900: "#1e3a8a",
950: "#172554"
}
}
}
},
plugins: []
}
// apps/my-app/tailwind-config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [],
darkMode: "class",
theme: {
extend: {
colors: {
primary: {
50: "#fef2f2",
100: "#fee2e2",
200: "#fecaca",
300: "#fca5a5",
400: "#f87171",
500: "#ef4444",
600: "#dc2626",
700: "#b91c1c",
800: "#991b1b",
900: "#7f1d1d",
950: "#450a0a"
}
}
}
},
plugins: []
}
Now the my-app colors configuration will overwrite the default ones.
Next Up in the Series
In this article, we set the foundation by creating a reusable UI layer. In the next part, we’ll explore adding a Drizzle into the database layer. Stay tuned for more ways to supercharge your SaaS factory with Nuxt 3 layers!