You're reading docs for Nextly Alpha. APIs may change between releases.

Configuration

Nextly Config Reference

Complete reference for every option in the nextly.config.ts configuration file.

The nextly.config.ts file is the central configuration for your Nextly application. The defineConfig() function validates your config, checks for duplicate slugs, and applies sensible defaults.

Full Example

nextly.config.ts
import { defineConfig } from '@nextlyhq/nextly';
import { s3Storage } from '@nextlyhq/storage-s3';
import Posts from './src/collections/posts';
import Media from './src/collections/media';
import SiteSettings from './src/singles/site-settings';
import Header from './src/singles/header';
import Footer from './src/singles/footer';
import { Seo, Hero } from './src/components';

export default defineConfig({
  // Content model
  collections: [Posts, Media],
  singles: [SiteSettings, Header, Footer],
  components: [Seo, Hero],

  // User model extensions
  users: {
    fields: [
      text({ name: 'company', label: 'Company' }),
      select({ name: 'department', options: [/* ... */] }),
    ],
  },

  // Output paths
  typescript: {
    outputFile: './src/types/generated/payload-types.ts',
    declare: true,
  },
  db: {
    schemasDir: './src/db/schemas/collections',
    migrationsDir: './src/db/migrations',
  },

  // Cloud storage
  storage: [
    s3Storage({
      bucket: process.env.S3_BUCKET!,
      region: process.env.AWS_REGION!,
      accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
      collections: {
        media: true,
      },
    }),
  ],

  // Security
  security: {
    cors: {
      origin: ['https://example.com'],
      credentials: true,
    },
    sanitization: { enabled: true },
  },

  // Rate limiting
  rateLimit: {
    enabled: true,
    readLimit: 100,
    writeLimit: 30,
    windowMs: 60000,
  },

  // Email
  email: {
    providerConfig: {
      provider: 'resend',
      apiKey: process.env.RESEND_API_KEY!,
    },
    from: 'My App <noreply@example.com>',
    baseUrl: 'https://example.com',
  },

  // Admin panel
  admin: {
    branding: {
      logoUrl: '/logo.svg',
      logoText: 'My App',
      favicon: '/favicon.ico',
      colors: {
        primary: '#6366f1',
        accent: '#f59e0b',
      },
    },
  },
});

Config Options Reference

collections

TypeCollectionConfig[]
Default[]

Array of collection configurations. Each collection should be defined with defineCollection() and imported from its own file. See Collections.

singles

TypeSingleConfig[]
Default[]

Array of single configurations for one-off documents like site settings, headers, and footers. Each single should be defined with defineSingle(). See Singles.

components

TypeComponentConfig[]
Default[]

Array of reusable component configurations that can be embedded in collections and singles via the component field type. Component slugs must be unique across components, collections, and singles. See Components.

users

TypeUserConfig
Defaultundefined

Extend the built-in user model with custom fields. Custom fields are stored in a separate user_ext table.

users: {
  fields: [
    text({ name: 'phoneNumber', label: 'Phone Number' }),
    text({ name: 'company', label: 'Company' }),
    select({ name: 'department', options: [/* ... */] }),
  ],
  admin: {
    listFields: ['company', 'department'],
  },
}

typescript

TypeTypeScriptConfig

Controls TypeScript type generation for your collections.

PropertyTypeDefaultDescription
outputFilestring'./src/types/generated/payload-types.ts'Path to the generated TypeScript file
declarebooleantrueWhether to add declare module blocks for type inference

db

TypeDatabaseConfig

Controls where Drizzle schema files and migration files are generated.

PropertyTypeDefaultDescription
schemasDirstring'./src/db/schemas/collections'Directory for generated Drizzle schema files
migrationsDirstring'./src/db/migrations'Directory for generated migration files

storage

TypeStoragePlugin[]
Default[]

Storage plugins for cloud storage providers. Local filesystem storage is used by default for collections without a configured plugin.

Available adapters:

  • @nextlyhq/storage-s3 -- AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces
  • @nextlyhq/storage-vercel-blob -- Vercel Blob Storage
import { s3Storage } from '@nextlyhq/storage-s3';

storage: [
  s3Storage({
    bucket: process.env.S3_BUCKET!,
    region: process.env.AWS_REGION!,
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    collections: {
      media: true,
      'private-docs': {
        prefix: 'private/',
        signedDownloads: true,
        clientUploads: true,
      },
    },
  }),
]

security

TypeSecurityConfig
DefaultSecure defaults applied by middleware

Controls security headers, CORS, file upload restrictions, and input sanitization.

PropertyTypeDescription
headersSecurityHeadersConfigCSP, X-Content-Type-Options, X-Frame-Options, HSTS, Referrer-Policy
corsCorsConfigCross-Origin Resource Sharing. Default: same-origin only
uploadsUploadSecurityConfigInputMIME type allowlist and SVG serving behavior
sanitizationSanitizationConfigInputHTML tag stripping, CSS validation, URL protocol validation
security: {
  cors: {
    origin: ['https://example.com', 'https://app.example.com'],
    credentials: true,
  },
  headers: {
    contentSecurityPolicy: "default-src 'self'",
  },
  uploads: {
    additionalMimeTypes: ['application/xml'],
  },
  sanitization: {
    enabled: true,
    stripHtmlFromText: true,
  },
}

rateLimit

TypeRateLimitingConfig
DefaultEnabled with 100 read / 30 write per minute

API rate limiting configuration. Enabled by default. Opt out with rateLimit: { enabled: false }.

PropertyTypeDefaultDescription
enabledbooleantrueEnable or disable rate limiting
readLimitnumber100Max GET requests per window
writeLimitnumber30Max POST/PATCH/PUT/DELETE requests per window
windowMsnumber60000Time window in milliseconds (1 minute)
storeRateLimitStoreIn-memoryCustom store (use Redis for multi-instance deployments)
keyGenerator(request: Request) => stringClient IPCustom key generation function
skip(request: Request) => booleanNoneSkip rate limiting for certain requests
collectionsRecord<string, { readLimit?, writeLimit? }>NonePer-collection rate limit overrides
rateLimit: {
  enabled: true,
  readLimit: 100,
  writeLimit: 30,
  collections: {
    media: { readLimit: 50, writeLimit: 10 },
    logs: { readLimit: 200 },
  },
}

apiKeys

TypeApiKeysConfig
Default1,000 requests/hour, 1-hour window

Per-key rate limiting for API key authentication (Authorization: Bearer sk_live_...).

PropertyTypeDefaultDescription
rateLimit.requestsPerHournumber1000Max requests per sliding window
rateLimit.windowMsnumber3600000Sliding window duration in ms (1 hour)

email

TypeEmailConfig
Defaultundefined

Email provider configuration for password resets, notifications, and other transactional emails. Database-managed providers (configured via admin Settings UI) take precedence when available.

email: {
  providerConfig: {
    provider: 'resend',
    apiKey: process.env.RESEND_API_KEY!,
  },
  from: 'My App <noreply@example.com>',
  baseUrl: 'https://example.com',
}

plugins

TypePluginDefinition[]
Default[]

Plugins extend Nextly with additional collections, hooks, and custom functionality.

import { formBuilder } from '@nextlyhq/plugin-form-builder';

const formBuilderPlugin = formBuilder();

export default defineConfig({
  plugins: [formBuilderPlugin.plugin],
  collections: [Posts],
});

admin

TypeAdminConfig
Defaultundefined (Nextly defaults used)

Admin panel branding and plugin sidebar customization.

admin.branding

PropertyTypeDefaultDescription
logoUrlstringNoneLogo image URL (replaces text logo)
logoUrlLightstringNoneLight-mode logo (when logoUrl is not set)
logoUrlDarkstringNoneDark-mode logo (when logoUrl is not set)
logoTextstring"Nextly"Text label in sidebar header
faviconstringNoneCustom favicon URL
colors.primarystringNonePrimary brand color (hex, e.g. "#6366f1")
colors.accentstringNoneAccent brand color (hex, e.g. "#f59e0b")
showBuilderbooleanNODE_ENV !== "production"Show/hide builder navigation in admin

admin.pluginOverrides

Override any plugin's sidebar placement and appearance without modifying plugin source code.

admin: {
  pluginOverrides: {
    'form-builder': {
      placement: AdminPlacement.SETTINGS,
      order: 80,
      appearance: { icon: 'FileText' },
    },
  },
}

Validation

defineConfig() performs these checks at startup:

  • Duplicate slugs -- Collection, single, and component slugs must all be unique
  • Cross-type conflicts -- A single cannot share a slug with a collection or component
  • Component nesting -- Circular component references and excessive nesting depth are rejected
  • User config -- Custom user fields are validated for correctness
  • API key limits -- requestsPerHour must be a positive integer

If validation fails, Nextly throws a descriptive error at startup so you can fix it immediately.

Next Steps