Skip to content

Configuration ​

nuxt.config.ts ​

Top-level configuration for the Nuxt 4 app.

Key Settings ​

SettingValueNotes
compatibilityDate'2025-06-30'Nuxt 4 stable compatibility date
future.compatibilityVersionnot setNuxt 4 file conventions enabled via compatibilityDate
ssrtrueSSR enabled
devtools.enabledtrueNuxt DevTools on in dev
devServer.host'0.0.0.0'Bind to all interfaces in dev
devServer.port5000Default dev server port; overridden by PORT env var
typescript.stricttrueTypeScript strict mode
typescript.typeCheckfalseRun pnpm typecheck separately (faster dev HMR)
features.inlineStylestrue in productionInline critical CSS in production builds only

Modules ​

ts
modules: [
  '@vueuse/nuxt',
  '@nuxt/icon',
  '@nuxt/eslint',
  '@pinia/nuxt',
]

Auto-Import Configuration ​

ts
imports: {
  dirs: [
    'composables/**',
    'utils/**',
    'stores/**',
    'constants/**',
  ]
}

app/types/ is intentionally excluded — always import types explicitly with import type.

Component auto-import:

ts
components: [
  {
    path: '~/components',
    pathPrefix: false,   // <AssetCard> not <ComponentsAssetCard>
  },
]

Route Rules ​

Route rules are defined at the top level (not inside nitro):

ts
routeRules: {
  '/': { prerender: true },
  '/**': { headers: { 'Cache-Control': 's-maxage=60' } },
  '/api/**': {
    cors: true,
    headers: { 'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE' },
  },
  '/_nuxt/**': {
    headers: { 'Cache-Control': 'public, max-age=31536000, immutable' },
  },
}

Vite ​

Key Vite settings:

ts
vite: {
  logLevel: 'warn',
  plugins: [vuetify({ autoImport: true })],
  css: {
    preprocessorOptions: {
      scss: {
        loadPaths: [
          path.resolve('./app/assets/scss'),
          path.resolve('./node_modules/vuetify/lib/styles'),
        ],
      },
    },
  },
  esbuild: {
    target: 'esnext',
    legalComments: 'none',
    drop: process.env.NODE_ENV === 'production' ? ['console', 'debugger'] : [],
  },
  build: {
    chunkSizeWarningLimit: 500,
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules/vuetify')) return 'vuetify'
          if (id.includes('node_modules/lodash-es')) return 'lodash'
          if (id.includes('node_modules/chart.js') || id.includes('node_modules/vue-chartjs')) return 'charts'
        },
      },
    },
  },
}

Vuetify 3 is loaded via vite-plugin-vuetify with tree-shaking enabled. Chunks are manually split for vuetify, lodash-es, and chart.js to keep the main bundle lean.

optimizeDeps.include pre-bundles: vue, pinia, @vueuse/core, vuetify, laravel-echo, pusher-js, dayjs, mitt, lodash-es, vue-chartjs, chart.js, @amplitude/analytics-browser, @amplitude/session-replay-browser, and others.

Nitro ​

ts
nitro: {
  compressPublicAssets: process.env.NODE_ENV === 'production',
  minify: process.env.NODE_ENV === 'production',
  sourceMap: false,
  prerender: { crawlLinks: false },
}

When APP_ENV=local, Nitro adds a dev proxy that forwards /api/v1/ to API_BASE_URL. This lets the frontend use a relative path and lets Nitro inject the correct Origin header for local backend CORS.

Icon Module ​

ts
icon: {
  mode: 'svg',
  provider: 'server',
  fallbackToApi: false,
  customCollections: [
    {
      prefix: 'collage',
      dir: './app/assets/icons',
      normalizeIconName: false,
    },
  ],
}

Custom icons live in app/assets/icons/ and are accessed as collage:icon-name. Client-bundle scanning is only enabled for production builds.

Build Transpile ​

ts
build: {
  transpile: ['vuetify', 'vue-chartjs', 'chart.js'],
}

Vue Compiler ​

ts
vue: {
  compilerOptions: {
    isCustomElement: (tag) => tag.startsWith('cropper-'),
  },
}

Treats <cropper-*> as native custom elements, avoiding Vue component lookup warnings.

App Head ​

Set from nuxt.config.ts at build time. Title comes from APP_NAME. Two Google fonts are loaded (Inter, Space Grotesk). The PWA manifest is served dynamically by server/routes/manifest.json.get.ts; no static favicon is set here — white-label hosts inject the favicon via useBrandingHead() at runtime.

Runtime Config ​

Server-only keys (never in client bundle):

ts
runtimeConfig: {
  apiBaseUrlServer: '',        // API_BASE_URL — absolute URL for SSR fetches
  typesenseHost: '',           // TYPESENSE_HOST
  typesensePort: '443',        // TYPESENSE_PORT
  typesenseProtocol: 'https',  // TYPESENSE_PROTOCOL
  typesenseApiKey: '',         // TYPESENSE_API_KEY
  typesenseConnectionTimeout: '10', // TYPESENSE_CONNECTION_TIMEOUT
  awsAccessKeyId: '',          // AWS_ACCESS_KEY_ID
  awsSecretAccessKey: '',      // AWS_SECRET_ACCESS_KEY
  awsBucket: '',               // AWS_BUCKET
  awsDefaultRegion: '',        // AWS_DEFAULT_REGION
  stripeSecretKey: '',         // STRIPE_SECRET_KEY
  stripeWebhookSecret: '',     // STRIPE_WEBHOOK_SECRET
  stripeWebhookEndpoint: '',   // STRIPE_WEBHOOK_ENDPOINT
  googleDriveClientSecret: '', // GOOGLE_DRIVE_CLIENT_SECRET
  dropboxAppSecret: '',        // DROPBOX_APP_SECRET
}

Public keys (available in both server and client):

ts
runtimeConfig: {
  public: {
    appName: '',                  // APP_NAME
    baseUrl: '',                  // BASE_URL
    apiBaseUrl: '',               // API_BASE_URL; '/api/v1/' when APP_ENV=local
    originUrl: '',                // ORIGIN_URL
    backendUrl: '',               // BACKEND_URL
    damBaseUrl: undefined,        // DAM_FRONTEND_URL
    userPlaceHolderImg: undefined, // USER_PLACEHOLDER_IMG
    googleAuthUrl: undefined,     // GOOGLE_AUTH_URL
    microsoftAuthUrl: undefined,  // MICROSOFT_AUTH_URL
    limitWorkspaceId: undefined,  // LIMIT_WORKSPACE_ID
    pusherKey: undefined,         // PUSHER_KEY
    pusherCluster: undefined,     // PUSHER_CLUSTER
    pusherAuthEndpoint: undefined, // PUSHER_AUTH_ENDPOINT
    appEnv: undefined,            // APP_ENV
    insightsAccessPersons: undefined, // INSIGHTS_ACCESS_PERSONS
    googleDriveClientId: undefined, // GOOGLE_DRIVE_CLIENT_ID
    redirectUri: undefined,       // REDIRECT_URI
    dropboxOauthUri: undefined,   // DROPBOX_OAUTH_URI
    dropboxAuthVerificationUri: undefined, // DROPBOX_AUTH_VERIFICATION_URI
    dropboxAppId: undefined,      // DROPBOX_APP_ID
    dropboxAuthRedirect: undefined, // DROPBOX_AUTH_REDIRECT
    mobileAppDomain: undefined,   // MOBILE_APP_DOMAIN
    damUploadAutotagEnabled: undefined, // DAM_UPLOAD_AUTOTAG_ENABLED
    supportBaseUrl: undefined,    // SUPPORT_BASE_URL
    isSupportPlatform: false,     // IS_SUPPORT_PLATFORM (parsed: === 'true')
    stripePublishableKey: undefined, // STRIPE_PUBLISHABLE_KEY
    stripeCheckoutMode: 'prebuilt', // STRIPE_CHECKOUT_MODE
    cname: '',                    // CNAME
    zipDownloadUrl: '',           // ZIP_DOWNLOAD_URL
    https: '',                    // HTTPS
    externalUploadLimit: '500MB', // EXTERNAL_UPLOAD_LIMIT
    queryStaleTime: 30000,        // NUXT_PUBLIC_QUERY_STALE_TIME (ms, overridable at runtime)
    amplitudeApiKey: '',          // AMPLITUDE_API_KEY
    amplitudeSessionReplaySampleRate: '', // AMPLITUDE_SESSION_REPLAY_SAMPLE_RATE (0–1)
  }
}

All values default to empty string / undefined / false in the config. They are populated from environment variables at runtime (.env locally, deployment env vars in production).

Environment Variables ​

Copy .env.example to .env and fill in values for local development. Never commit .env.

Required ​

VariableUsed by
API_BASE_URLServer-side fetches (apiBaseUrlServer); Nitro dev proxy target when APP_ENV=local
APP_NAME<title> tag and appName public config
APP_ENVEnables local dev proxy when set to local; exposed as appEnv
BASE_URLPublic base URL of the app
BACKEND_URLDNS prefetch/preconnect hint in <head>
PUSHER_KEYLaravel Echo / Pusher client
PUSHER_CLUSTERLaravel Echo / Pusher client
PUSHER_AUTH_ENDPOINTPrivate channel authentication
TYPESENSE_HOSTNitro Typesense proxy
TYPESENSE_API_KEYNitro Typesense proxy
AWS_ACCESS_KEY_IDNitro S3 proxy
AWS_SECRET_ACCESS_KEYNitro S3 proxy
AWS_BUCKETNitro S3 proxy
AWS_DEFAULT_REGIONNitro S3 proxy
STRIPE_PUBLISHABLE_KEYClient-side Stripe.js
STRIPE_SECRET_KEYServer-side Stripe API
AMPLITUDE_API_KEYAmplitude analytics plugin

Optional / Feature-specific ​

VariableDefaultUsed by
PORT5000Dev server port
TYPESENSE_PORT443Typesense proxy
TYPESENSE_PROTOCOLhttpsTypesense proxy
TYPESENSE_CONNECTION_TIMEOUT10Typesense proxy
AMPLITUDE_SESSION_REPLAY_SAMPLE_RATE0Session Replay sampling rate (0–1)
STRIPE_WEBHOOK_SECRET—Stripe webhook verification
STRIPE_WEBHOOK_ENDPOINT—Stripe webhook endpoint
STRIPE_CHECKOUT_MODEprebuiltStripe checkout mode
GOOGLE_AUTH_URL—Google SSO
GOOGLE_DRIVE_CLIENT_ID—Google Drive integration
GOOGLE_DRIVE_CLIENT_SECRET—Google Drive integration (server-only)
GOOGLE_SITE_VERIFICATION—Google Search Console meta tag
MICROSOFT_AUTH_URL—Microsoft SSO
REDIRECT_URI—OAuth redirect URI
DROPBOX_APP_ID—Dropbox integration
DROPBOX_APP_SECRET—Dropbox integration (server-only)
DROPBOX_OAUTH_URI—Dropbox OAuth start URL
DROPBOX_AUTH_VERIFICATION_URI—Dropbox OAuth callback
DROPBOX_AUTH_REDIRECT—Dropbox redirect after auth
DAM_FRONTEND_URL—Link back to the DAM frontend
USER_PLACEHOLDER_IMG—Fallback avatar URL
LIMIT_WORKSPACE_ID—Restricts UI to a single workspace
INSIGHTS_ACCESS_PERSONS—Comma-separated user IDs with Analytics access
ZIP_DOWNLOAD_URL—Backend ZIP download endpoint
ORIGIN_URL—Override origin injected into user API calls
MOBILE_APP_DOMAIN—Apple Smart App Banner app-argument
DAM_UPLOAD_AUTOTAG_ENABLED—Enables AI auto-tagging on upload
SUPPORT_BASE_URL—Link to support docs
IS_SUPPORT_PLATFORMfalseRenders the support-team variant of the login layout
CNAME—Custom domain CNAME
HTTPS—Forces HTTPS redirects
EXTERNAL_UPLOAD_LIMIT500MBFile size cap shown to external uploaders
NUXT_PUBLIC_QUERY_STALE_TIME30000TanStack Query staleTime in ms (overridable at runtime without redeploy)
LOG_LEVEL—Server log verbosity (0=silent … 3=info)
CORS_ALLOWED_ORIGINS—Server CORS origin allowlist

Server-only (not in runtimeConfig, used in server routes) ​

Some variables are consumed directly by Nitro server routes and do not flow through runtimeConfig. These include BOX_CLIENT_ID, BOX_CLIENT_SECRET, BOX_AUTH_REDIRECT, TWILIO_SID, TWILIO_TOKEN, OPENAI_KEY, OPENAI_PER_API_IMAGE_GENERATE_COUNT, and G_PLACES_API_KEY.

ESLint Configuration ​

eslint.config.ts provides project-wide lint rules. Run: pnpm lint

TypeScript Configuration ​

tsconfig.json is generated by Nuxt. Key settings:

  • strict: true
  • verbatimModuleSyntax: true — enforces import type for type-only imports
  • Path alias ~ → ./app

Run pnpm typecheck to type-check without building.

  • Plugins — runtimeConfig consumption
  • Server API — server-side secret usage
  • Styling — SCSS configuration