Byline CMS
  • Home
  • Docs
  • About
Byline CMS
View on GitHub
  • Getting Started
    • Overview
    • CLI
    • Development environment and example application
    • Configuration
    • Upgrading from 3.21 to 4.x
  • Why Byline
    • Overview
    • Mission & Vision
    • Content Management in the Time of AI
  • Key Architectural Decisions
    • Overview
    • Core Document Storage
    • Core Composition
    • Transactions
    • Path Grammar
    • Deployment Topologies
  • Collections
    • Overview
    • Fields
    • Blocks
    • Relationships
    • Document Trees
    • Document Paths
    • File / Media Uploads
    • Rich Text Editor
    • Collection Versioning
  • Reading & Delivery
    • Overview
    • Search & Document Extraction
    • Client SDK (@byline/client)
    • Routing & API
    • Transports
    • Markdown Export
    • MCP Server
    • Caching
  • Search
    • Overview
    • Configure search
    • Indexing and reindexing
    • Search API
    • Search provider contract
    • Portable multilingual search analysis
    • PostgreSQL and MySQL search providers
    • Attachment extraction for search
  • Auth & Security
    • Overview
    • Authentication & Authorization
    • Auditability
  • Internationalization (i18n)
    • Overview
    • The host i18n system
    • Admin interface translations
    • Content locales
    • Administering content locales
  • Admin UI
    • Overview
    • UI Kit (@byline/ui)
    • Client-config registration
  • API Reference
    • Overview
    • Configuration API
    • Collections API
    • Fields API
    • Client SDK API
  • Testing
  • Home

Configuration

Companions:

  • Configuration API reference — every BaseConfig, ClientConfig, and ServerConfig property, including defaults and runtime requirements.
  • Collections — how the collection tuple divides each content type into an isomorphic schema and an admin presentation config.
  • Core composition — how initBylineCore() validates and composes the server-side adapters registered here.
  • Client-config registration — why the admin config is registered from both the eager and lazy _byline route graphs.

Byline configuration is application-owned code that tells the CMS what content exists, how editors work with it, and which server implementations provide storage, authentication, search, and rich text. In the reference application, these files live under apps/webapp/byline.

This split is slightly more involved than a single monolithic configuration file, but it gives each runtime a focused, type-checked surface and keeps server-only and admin-only dependencies out of the wrong bundles.

The important rule is that this directory crosses four different runtime boundaries. A value that belongs in one boundary can break builds, leak server code, or make scripts unusable when it is imported from another.

Configuration boundaries

Boundary

What belongs there

What must stay out

Isomorphic schema

Plain collection and field definitions, serializable adapter settings, isomorphic-safe functions

React, CSS modules, Node-only modules, browser globals

Server bootstrap

Database, storage, sessions, search, server-only hooks, rich-text server adapters

React components and admin presentation

Admin module graph

Collection presentation, field widgets, block presentation, editor components

Database adapters, secrets, Node-only modules

Public client facade

Locale and route data needed by the host frontend

Admin translations, editor code, server configuration

The same collection schemas are imported by the server bootstrap and the browser admin. They are therefore isomorphic, not merely server-safe: every reachable schema import must work in both environments.

Files at a glance

Files you normally edit

File or directory

Boundary

Purpose

collections/<name>/schema.ts

Isomorphic schema

Defines one collection's fields, workflow, search projection, paths, hooks, and structural options.

collections/<name>/admin.tsx

Admin module graph

Defines columns, layouts, preview URLs, custom list views, and per-field UI overrides for that collection.

collections/index.ts

Isomorphic schema

Exports the single canonical collection tuple consumed by code generation, the server, and the admin.

collections/server-hooks.ts

Server bootstrap

Registers collection and upload hooks whose modules import server-only code.

blocks/*.ts

Isomorphic schema

Defines reusable block schemas.

blocks/*.admin.tsx

Admin module graph

Defines block-scoped field presentation.

server.config.ts

Server bootstrap

Calls initBylineCore() with the database, storage, admin store, session provider, search provider, server hooks, and rich-text server adapters.

admin.config.ts

Admin module graph

Calls defineClientConfig() with collection admin configs, block admin configs, field editors, routes, and i18n.

locales.ts

Public client safe

Declares the host's admin-interface and content locale sets as dependency-free data.

i18n.ts

Shared configuration

Assembles locale sets and admin translation bundles for the server and admin configs. Do not import it into public browser code.

routes.ts

Public client safe

Resolves the admin, API, and sign-in mount paths once for both configs and the host frontend.

public.ts

Public client safe

Re-exports the small set of Byline route and locale values that public host code may import.

Tooling and contract files

File or directory

Purpose

load-env.ts

Loads .env.local and .env for Node scripts that do not run through Vite. Import it before server.config.ts.

seed.ts, seed-admin.ts, seed-docs.ts

Initialize the server config and run application-owned seed routines. They are operational entry points, not configuration APIs.

seeds/

Contains the reusable seed implementations invoked by the entry points.

scripts/

Contains code generation, documentation import, and maintenance commands.

collection-types.contract.ts

Proves at compile time that generated collection types exactly match the canonical collection tuple.

registered-client-types.test.node.ts

Verifies that generated declaration merging types the unparameterized client and server client getters.

async-hooks.browser.ts

Supplies the browser no-op used by the Vite alias for node:async_hooks. Application configuration does not import it directly.

generated/collection-types.ts

Generated collection type projection. Run pnpm byline:generate; never edit it by hand.

How configuration loads

Server startup

apps/webapp/src/server.ts imports byline/server.config.ts as a side effect. The module constructs the selected adapters, calls initBylineCore(), and waits for configuration validation and collection reconciliation before the server handles requests.

src/server.ts
-> byline/server.config.ts
-> collections/index.ts
-> collections/server-hooks.ts
-> i18n.ts + routes.ts
-> database / storage / auth / search adapters
-> initBylineCore()

Node scripts use the same bootstrap after loading environment variables:

import './load-env.js'
import './server.config.js'
import { getSystemBylineClient } from '@byline/client/server'
const client = getSystemBylineClient()

initBylineCore() is asynchronous. The scaffolded server.config.ts uses top-level await, so any module that imports it continues only after the core has initialized.

Admin registration

admin.config.ts is not imported by the public application root. The _byline route loads it from two complementary locations:

  • route.tsx dynamically imports it from beforeLoad, which protects child loaders.
  • route.lazy.tsx imports it for initial hydration and component rendering.

Both paths call defineClientConfig() against their own Vite module graph. Keeping those imports under _byline/* prevents admin and editor code from entering public bundles.

Public application code

Public browser code imports byline/public.ts, not admin.config.ts, server.config.ts, or i18n.ts:

import { contentLocales, routes } from '../../byline/public.js'
const docsUrl = `${routes.admin}/collections/docs`
const supportedContentLocales = contentLocales.map((locale) => locale.code)

The facade is intentionally small. Add a public export only when the public host genuinely needs the value and its complete import graph is safe for the browser.

Configure the server

The reference server config composes one installation-wide runtime:

const core = await initBylineCore<AdminStore>({
serverURL,
i18n,
routes,
collections,
hooks: serverHooks,
db,
adminStore,
storage: localStorageProvider({ uploadDir: './uploads', baseUrl: '/uploads' }),
sessionProvider,
fields: {
richText: {
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
populate: lexicalEditorPopulateServer({ getClient: getAdminBylineClient }),
toMarkdown: lexicalEditorToMarkdownServer(),
toText: lexicalEditorToTextServer(),
},
},
search: postgresSearch({ pool: db.pool, defaultLocale: i18n.content.defaultLocale }),
})

initBylineCore() validates the combined configuration before it registers the replacement singleton. For example, search-enabled collections require a search provider, relation-bearing rich-text modes require their matching server adapters, and every configured admin locale requires translations.

The complete property contract is in the Configuration API reference.

Configure the admin

The client config registers presentation and browser-side adapters over the same schema tuple:

export const config: ClientConfig = {
serverURL,
i18n,
routes,
collections,
admin: [DocsAdmin, NewsAdmin, PagesAdmin, MediaAdmin, NewsCategoriesAdmin],
blockAdmin: [QuoteBlockAdmin, PhotoBlockAdmin, FAQBlockAdmin],
fields: {
richText: { editor: LexicalRichTextAi },
},
}
defineClientConfig(config)

The server and client configs share serverURL, i18n, routes, and collections. They do not extend each other: server-only adapters belong only on ServerConfig, while React-bearing presentation belongs only on ClientConfig.

The complete property contract is in the Configuration API reference.

Add a collection

Create the schema first and keep it isomorphic:

// apps/webapp/byline/collections/articles/schema.ts
import { defineCollection } from '@byline/core'
export const Articles = defineCollection({
path: 'articles',
labels: { singular: 'Article', plural: 'Articles' },
useAsTitle: 'title',
useAsPath: 'title',
fields: [
{ name: 'title', type: 'text', localized: true },
{ name: 'content', type: 'richText', localized: true },
],
})

Then add the admin presentation:

// apps/webapp/byline/collections/articles/admin.tsx
import { defineAdmin } from '@byline/core'
import { Articles } from './schema.js'
export const ArticlesAdmin = defineAdmin(Articles, {
columns: [
{ fieldName: 'title', label: 'Title', sortable: true },
{ fieldName: 'status', label: 'Status' },
],
layout: { main: ['title', 'content'] },
})

Finally, add Articles to collections/index.ts and ArticlesAdmin to the admin array in admin.config.ts. Run pnpm byline:generate after changing the schema, and commit the generated type projection.

The Collections API reference lists every collection and admin property. The Fields API reference lists every built-in field type.

Common boundary mistakes

Importing admin config from a public route

This pulls collection presentation, React components, and editor extensions into the public graph. Import byline/public.ts for route and locale data instead.

Importing server-only code from a schema

The schema also enters the browser admin bundle. Register hooks that depend on Node, secrets, server clients, or backend SDKs through collections/server-hooks.ts and ServerConfig.hooks.

Importing i18n.ts from public browser code

i18n.ts assembles admin translation bundles. Public code should import the dependency-free locale arrays through public.ts.

Changing only routes.ts

The route config changes the canonical URLs consumed by Byline; it does not rename the host application's physical TanStack route files. Use the CLI setup flow when moving an existing admin or sign-in mount so both sides stay synchronized.

Next

Use the Configuration API reference when you need an exact property or function signature. Continue to Collections when you are ready to model a content type, or to the Client SDK when you are ready to query it.

PreviousDevelopment environment and example application
NextUpgrading from 3.21 to 4.x

On this Page

  • Configuration boundaries
  • Files at a glance
  • Files you normally edit
  • Tooling and contract files
  • How configuration loads
  • Server startup
  • Admin registration
  • Public application code
  • Configure the server
  • Configure the admin
  • Add a collection
  • Common boundary mistakes
  • Importing admin config from a public route
  • Importing server-only code from a schema
  • Importing i18n.ts from public browser code
  • Changing only routes.ts
  • Next
Byline CMS

Building the future of content management, one commit at a time.

Project

  • Documentation
  • Roadmap
  • Contributing
  • Releases

Community

  • GitHub Discussions
  • Blog
  • Newsletter

Legal

  • Privacy Policy
  • Terms of Use
  • Cookies

© 2026 Infonomic Company Limited and contributors. Open source and built with ❤️ by the community.