Github
Configuration

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> or HANDOFF_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, and checkout also 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.include also controls what make:component and checkout register.
  • A selected profile also reads .env.<profile> on top of .env. Handoff reads it once at startup and does not watch it. It cannot set HANDOFF_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:

handoff.config.local.ts
import { defineConfig } from 'handoff-app'

export default defineConfig({
  app: { title: 'Design System (local)' },
})
.env
HANDOFF_PROFILE=local

Registry build profile

Put the registry build settings in a profile so the base config stays a workspace config:

handoff.config.registry.ts
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') },
      },
    },
  },
})
Terminal
npm run build -- --target registry --profile registry

build --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:

Terminal
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.staging and .env.production can each set HANDOFF_REGISTRY_URL and HANDOFF_REGISTRY_ACCESS_TOKEN.

See Connect a workspace.

On this page