Building a custom admin project
Building a custom admin project
You've got the base layer running (see Getting started). Now you want a project admin with your brand, your modules, and your backend — the way redelay/admin sits on top of redelay/js-admin-nuxt4.
extends to a sibling clone (Mode A) — simplest for hacking inside
the monorepo. For a real project, prefer the versioned dependency
(@redelay/js-admin pinned to a tag, with a local dev override): a fresh clone
or CI builds without the base-layer tree beside it, and the version is pinned.
See Consuming the base layer (versioned) — the
canonical approach for new projects.1. Scaffold
Sit the project admin as a sibling of the base layer in the same monorepo:
your-monorepo/
├── js-admin-nuxt4/ # clone of redelay/js-admin-nuxt4
└── your-admin/ # this one
├── package.json
├── nuxt.config.ts
├── app.config.ts
├── tailwind.config.ts
├── assets/css/tailwind.css
└── modules/
package.json
{
"name": "your-admin",
"private": true,
"type": "module",
"scripts": {
"dev": "nuxt dev --port 3001",
"build": "nuxt build",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@iconify-json/heroicons": "^1.2.3",
"@iconify-json/lucide": "^1.2.73",
"@nuxt/ui": "^4.1.0",
"@pinia/nuxt": "^0.11.3",
"nuxt": "^4.2.1",
"nuxt-open-fetch": "^0.13.6",
"pinia": "^3.0.4"
}
}
nuxt.config.ts
Copy the skeleton from layered architecture. Point extends at your sibling clone of js-admin-nuxt4.
assets/css/tailwind.css
The base layer's css: ['~/assets/css/tailwind.css'] resolves ~ to this project at runtime. That means you MUST provide this file. Don't @import the base's stylesheet; duplicate its imports and add @source globs pointing at both trees:
@import "tailwindcss";
@import "@nuxt/ui";
/* Project-owned sources */
@source "../../components/**/*";
@source "../../layouts/**/*";
@source "../../pages/**/*";
@source "../../modules/**/*";
/* Inherited base-layer sources */
@source "../../../js-admin-nuxt4/components/**/*";
@source "../../../js-admin-nuxt4/layouts/**/*";
@source "../../../js-admin-nuxt4/pages/**/*";
@source "../../../js-admin-nuxt4/modules/**/*";
/* Brand colour tokens */
:root {
--tblr-primary: #8B5CF6; /* your brand colour here */
}
tailwind.config.ts (empty stub)
Required by @nuxt/ui v4 for Tailwind bootstrapping; @source directives in the CSS file drive the actual scan:
export default { content: [], theme: { extend: {} } }
app.config.ts — brand theme
export default defineAppConfig({
ui: {
colors: {
primary: 'violet', // or indigo, cyan, emerald, …
neutral: 'slate',
},
},
})
The primary colour feeds every color="primary" button/badge/link. Nuxt UI ships 22 named palettes.
2. Dockerise (optional, recommended)
The base layer's Dockerfile.admin is a plain node:20-alpine + pnpm; reuse it. The compose service wires the two layer paths as separate bind mounts so hot-reload works across both:
# infra/docker-compose.yml
admin:
build:
context: ..
dockerfile: infra/dockerfiles/Dockerfile.admin
working_dir: /app/your-admin
command: sh -c "test -d node_modules/nuxt || pnpm install --no-frozen-lockfile; pnpm dev --host 0.0.0.0 --port 3001"
ports: ["3001:3001"]
environment:
NUXT_API_URL: http://localhost:8001
NUXT_API_INTERNAL_URL: http://admin-api:8001
volumes:
- ../your-admin:/app/your-admin
- ../js-admin-nuxt4:/app/js-admin-nuxt4
- your-admin-node-modules:/app/your-admin/node_modules
depends_on:
- admin-api
Do not mount a named volume for js-admin-nuxt4/node_modules — it forces a second install and creates the pnpm duplicate-instance problem. The base layer is source-only at runtime.
3. First login
Start the stack and navigate to http://localhost:3001. The base layer's /login page appears — it's inherited through the layer. Sign in with your backend's credentials.