Profiles
Keep one base config and merge a small profile file on top of it per environment or per registry.
A profile is a config file that merges onto the base config. It holds only what changes between environments, so a project keeps one shared config instead of several full copies.
Rules
- The base config is
handoff.config.ts,.js,.cjs, or.json. Without a profile, Handoff loads only this file. - A profile file is
handoff.config.<profile>.*, with the same four extensions. - Select a profile with
--profile <name>orHANDOFF_PROFILE. The flag wins. - Names use lowercase letters, numbers, and hyphens. None are predefined.
- A selected profile must exist, or the command stops with an error. Exception:
login,logout,publish, andcheckoutalso accept a profile that has only a saved registry login. - Layers apply in order: defaults, base config, profile, programmatic config. Arrays replace, so a
profile that sets
catalog.includealso controls whatmake:componentandcheckoutregister. - A selected profile also reads
.env.<profile>on top of.env. Handoff reads it once at startup and does not watch it. It cannot setHANDOFF_WORKING_PATH.
defineConfig types a profile the same way as a base config.
Local profile
The .gitignore that init writes lists handoff.config.local.*, so a local profile stays on
one machine:
import { defineConfig } from 'handoff-app'
export default defineConfig({
app: { title: 'Design System (local)' },
})HANDOFF_PROFILE=localRegistry build profile
Put the registry build settings in a profile so the base config stays a workspace config:
import { defineConfig, fromEnv } from 'handoff-app'
export default defineConfig({
runtime: {
registry: {
database: { url: fromEnv('DATABASE_URL'), driver: 'neon' },
email: {
from: 'Handoff <no-reply@example.com>',
provider: 'smtp',
options: { host: fromEnv('SMTP_HOST') },
},
},
},
})npm run build -- --target registry --profile registrybuild --target registry always builds a registry-mode app, so the profile does not set
runtime.mode. On a host or in CI, set HANDOFF_PROFILE=registry instead of the flag.
One workspace, several registries
Each profile keeps its own registry login:
npm run login -- --profile staging --url https://staging.example.com
npm run login -- --profile production --url https://registry.example.com
npm run publish -- all --profile staging
npm run checkout -- catalog button --profile production- A login profile needs no
handoff.config.<name>.*file. - A profile without its own login uses the default login.
- Instead of logins,
.env.stagingand.env.productioncan each setHANDOFF_REGISTRY_URLandHANDOFF_REGISTRY_ACCESS_TOKEN.
See Connect a workspace.