Manual installation
For an interactive setup that creates the configurable application for you, use Installation.
This guide creates a new portal with the shared platform, Timesheets, Invoices, and the Invoice Timesheets bridge. Service Requests is an optional example extension and is not needed here.
For an existing Nuxt application, add the packages and merge the configuration and app entry below. To work on this repository itself, use the contribution guide.
Requirements and release
- Node.js 22.19 or newer on a Nuxt-compatible LTS release;
- a running PostgreSQL server and an empty database you can connect to;
- pnpm, npm, Yarn, or Bun.
The examples use the published package version 0.3.1. Keep every official package on this same version and review the compatibility guidance before upgrading.
1. Create the application
Create an empty directory and enter it:
mkdir my-customer-portal
cd my-customer-portal
Add the following file. The portal script loads .env for the kit CLI; Nuxt loads .env itself during development.
{
"name": "my-customer-portal",
"private": true,
"type": "module",
"scripts": {
"dev": "nuxt dev --host localhost --port 3000",
"build": "nuxt build",
"postinstall": "nuxt prepare",
"portal": "node --env-file=.env ./node_modules/@nuxt-customer-portal/kit/bin/nuxt-customer-portal.mjs"
},
"dependencies": {
"nuxt": "^4.5.1",
"vue": "^3.5.0",
"@nuxt/ui": "^4.10.0",
"tailwindcss": "^4.3.3"
}
}
Add .env, node_modules/, .nuxt/, and .output/ to your .gitignore.
2. Install the portal packages
Choose one package manager. Each command also installs the dependencies declared above.
pnpm add @nuxt-customer-portal/preset@0.3.1 @nuxt-customer-portal/kit@0.3.1 @nuxt-customer-portal/timesheets@0.3.1 @nuxt-customer-portal/invoices@0.3.1 @nuxt-customer-portal/invoice-timesheets@0.3.1
npm install @nuxt-customer-portal/preset@0.3.1 @nuxt-customer-portal/kit@0.3.1 @nuxt-customer-portal/timesheets@0.3.1 @nuxt-customer-portal/invoices@0.3.1 @nuxt-customer-portal/invoice-timesheets@0.3.1
yarn add @nuxt-customer-portal/preset@0.3.1 @nuxt-customer-portal/kit@0.3.1 @nuxt-customer-portal/timesheets@0.3.1 @nuxt-customer-portal/invoices@0.3.1 @nuxt-customer-portal/invoice-timesheets@0.3.1
bun add @nuxt-customer-portal/preset@0.3.1 @nuxt-customer-portal/kit@0.3.1 @nuxt-customer-portal/timesheets@0.3.1 @nuxt-customer-portal/invoices@0.3.1 @nuxt-customer-portal/invoice-timesheets@0.3.1
The preset supplies authentication, organizations, client management, administration, and the neutral portal UI. Timesheets and Invoices can also be used independently; omit the bridge if you do not need to invoice recorded time.
3. Compose the layers
import { definePortalConfig } from '@nuxt-customer-portal/kit'
export default definePortalConfig({
clients: { defaultModules: ['timesheets', 'invoices'] },
layers: [
'@nuxt-customer-portal/preset',
'@nuxt-customer-portal/timesheets',
'@nuxt-customer-portal/invoices',
'@nuxt-customer-portal/invoice-timesheets'
]
})
import portal from './portal.config'
export default defineNuxtConfig({
extends: portal.nuxtLayers,
css: ['~/assets/css/main.css'],
runtimeConfig: {
public: { clients: portal.clients }
}
})
defaultModules enables these modules for newly created clients. It does not assign reviewers or invoice viewers. See client access before sharing records.
Create the app entry and stylesheet, including their parent directories:
<template>
<UApp>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</UApp>
</template>
@import 'tailwindcss';
@import '@nuxt/ui';
UApp supplies the UI context used by toasts and overlays; NuxtLayout renders the portal's fallback shell. You can customize the host after the initial setup works.
4. Configure the database and environment
Create an empty PostgreSQL database using your database administration tool. The database user must be able to create schemas and tables. Replace the credentials in the example URL with your own.
Generate a Better Auth secret:
openssl rand -base64 32
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/customer_portal
PUBLIC_URL=http://localhost:3000
BETTER_AUTH_URL=http://localhost:3000
BETTER_AUTH_SECRET=<paste-the-generated-secret>
PORTAL_REGISTRATION_MODE=invitation-only
Keep both URLs and the development server on localhost:3000. If you change the host or port, update both URL variables as well.
5. Migrate and create the owner
Run the following from your application directory. For npm, replace pnpm run portal with npm run portal --; for Yarn or Bun, use yarn run portal or bun run portal.
pnpm run portal doctor
pnpm run portal db status
pnpm run portal db migrate
pnpm run portal provider seed --organization-name "My Agency" --organization-slug my-agency --user-name "Portal Owner" --user-email owner@example.com --user-password '<choose-a-unique-password>'
doctor checks installed providers, their dependencies, and default client modules. db migrate applies their migrations in dependency order.
The seed command creates the provider organization and an owner account. A newly seeded account is already email-verified, so you can sign in for the local evaluation before configuring email. Repeating the seed with the same organization slug and email reuses those records; it does not reset an existing user's password.
This section is for a new database. For an existing installation, review database migrations and upgrade recovery before applying changes.
6. Sign in and try the workflow
pnpm run dev
Use npm run dev, yarn run dev, or bun run dev if that is your chosen manager. Open http://localhost:3000/login and sign in with the seeded credentials. Select your provider organization if needed, then open the dashboard.
Follow your first portal workflow to add a client, record time, and create an invoice. Configure email delivery before inviting team members or clients, verifying new accounts, or sending invoice emails.
If setup fails, check troubleshooting. Before going live, review deployment, branding, and backup procedures.
