Skip to content

SSR Config

This page explains the two separate config layers inside @lentystyle/ssr:

  • LentySsrConfig for request-time rendering
  • the project-level ssr block inside luis.config.mjs

Terminal window
pnpm add @lentystyle/ssr

LentySsrConfig is the public config type passed to renderLentySsrSnapshot(), createLentySsrIntegration(), and other request-time SSR surfaces.

This layer:

  • defines runtime option defaults
  • carries HTML injection metadata
  • holds fields like mode, adapter, and rewritePolicy in resolved form

interface LentySsrConfig {
mode?: 'static' | 'ssr'
adapter?: 'none' | 'vue' | 'svelte' | 'react' | 'astro'
performance?: boolean
debug?: boolean
map?: boolean
lazy?: boolean
worker?: 'auto' | 'main' | 'worker'
assetBaseUrl?: string
globalCssHref?: string
bootstrapScriptSrc?: string
runtimeOptions?: string | Partial<LentySsrRuntimeOptions>
rewritePolicy?: 'auto' | 'prefer-static' | 'prefer-runtime'
}
FieldDefault
mode'ssr'
adapter'none'
debugfalse
performancefalse
mapfalse
lazyfalse
worker'auto'
assetBaseUrl/_hybrid
globalCssHrefnull
bootstrapScriptSrcnull
rewritePolicy'auto'
output.payloadMode'inline-json'
import { createLentySsrIntegration } from '@lentystyle/ssr'
const ssr = createLentySsrIntegration({
mode: 'ssr',
globalCssHref: '/assets/site.css',
bootstrapScriptSrc: '/assets/browser-entry.js',
runtimeOptions: 'performance,!worker',
})

In this example:

  • the common global CSS is injected as <link rel="stylesheet"> on every render
  • the bootstrap script is injected on every render
  • the runtime options resolution gives performance: true and worker: 'main'

SSR request helpers come with their own internal guard policy; you do not pass guardPolicy through config.

SurfaceGuard behavior
renderLentySsrSnapshot()preset: 'strict', htmlMode: 'ssr'
createLentySsrIntegration().renderRequest()preset: 'strict', htmlMode: 'ssr'
createLentySsrFrameworkAdapter().render()preset: 'strict', htmlMode: 'ssr'

Short example:

import { renderLentySsrSnapshot } from '@lentystyle/ssr'
await renderLentySsrSnapshot({}, {
routeId: '/docs/',
html: '<html><head></head><body><div class="card"></div></body></html>',
sources: [
{
sourceId: 'docs.luis',
source: '.card { color: #0f172a; }',
},
],
})

Even if you do not provide extra guard config, this call validates the request envelope and maintains strict validation in the downstream hybrid compile step.


The runtime option merge order inside SSR is fixed:

package defaults
-> config.debug / performance / map / lazy / worker
-> config.runtimeOptions
-> source.runtimeOptions
  1. Base defaults are applied

    {
    debug: false,
    performance: false,
    map: false,
    lazy: false,
    worker: 'auto',
    }
  2. Top-level config flags are applied

    debug, performance, map, lazy, and worker are applied at this stage.

  3. config.runtimeOptions patch is applied

    String or object patch overrides top-level flags.

  4. Source-level override is applied

    input.sources[].runtimeOptions overrides the config-level result per source.

runtimeOptions: 'debug,performance,!worker'

Result:

{
debug: true,
performance: true,
map: false,
lazy: false,
worker: 'main',
}

Supported tokens:

  • debug, performance, map, lazy
  • !debug, !performance, !map, !lazy
  • workerworker: 'worker'
  • !workerworker: 'main'
const result = await renderLentySsrSnapshot(
{ runtimeOptions: 'debug,worker' },
{
routeId: '/docs/',
html,
sources: [
{
sourceId: 'docs.luis',
source,
runtimeOptions: '!debug,lazy,!worker',
},
],
},
)

Final runtime options for this source:

{
debug: false,
performance: false,
map: false,
lazy: true,
worker: 'main',
}

Request-level default and source-level override together

Section titled “Request-level default and source-level override together”

Config:

const config = {
runtimeOptions: 'debug,performance,worker',
}

Source:

const input = {
routeId: '/docs/',
html: '<html><head></head><body><div class="card"></div></body></html>',
sources: [
{
sourceId: 'docs.luis',
source: '.card { color: #0f172a; }',
runtimeOptions: '!debug,lazy,!worker',
},
],
}

Final runtime options for this source:

{
debug: false,
performance: true,
map: false,
lazy: true,
worker: 'main',
}

The project-level SSR config type is LuisSsrProjectConfig. It inherits LentySsrConfig fields and adds preview or project-level fields.

import { defineLuisConfig } from '@lentystyle/core'
export default defineLuisConfig({
ssr: {
mode: 'ssr',
outDirName: 'dist-ssr',
maxEntries: 100,
defaultRuntimeOptions: '',
include: ['docs/**', '**/*.html'],
exclude: ['admin/**'],
},
})
FieldPurposeDefault
outDirNameName of the preview HTML root directory'dist-ssr'
maxEntriesApp-level cache limit for prod helper100
defaultRuntimeOptionsDefault runtime option string merged into project-level sources''
includeRelative HTML path patterns to include[]
excludeRelative HTML path patterns to skip[]
import { defineLuisConfig } from '@lentystyle/core'
export default defineLuisConfig({
ssr: {
mode: 'ssr',
outDirName: 'dist-ssr',
defaultRuntimeOptions: 'performance',
include: ['docs/**'],
},
})

In this setup defaultRuntimeOptions gives a common baseline to auto-discovered sources. If a source needs different behavior, override it with source-level runtimeOptions.


loadLuisSsrProjectConfig(configOrPath?, overrides?)

Section titled “loadLuisSsrProjectConfig(configOrPath?, overrides?)”
import { loadLuisSsrProjectConfig } from '@lentystyle/ssr'
const config = await loadLuisSsrProjectConfig(undefined, { cwd: appDir })

This helper:

  • loads the luis.config.mjs file
  • reads the ssr block
  • resolves request-level config fields
  • applies project-level defaults like outDirName, maxEntries, include, and exclude

Validation rules:

  • throws if the config file does not export a real ssr object
  • if include and exclude are provided, they must be string arrays
  • outDirName cannot be empty and cannot contain / or \
{
configPath: '.../luis.config.mjs',
configDir: '.../apps/site',
outDirName: 'dist-ssr',
outDir: '.../apps/site/dist-ssr',
maxEntries: 100,
defaultRuntimeOptions: '',
include: [],
exclude: [],
runtimeOptions: {
debug: false,
performance: false,
map: false,
lazy: false,
worker: 'auto',
},
}

shouldProcessLuisSsrProjectHtmlFile(htmlFilePath, config) converts the generated HTML path to a relative path under config.outDir and matches patterns.

import {
loadLuisSsrProjectConfig,
shouldProcessLuisSsrProjectHtmlFile,
} from '@lentystyle/ssr'
const config = await loadLuisSsrProjectConfig(undefined, { cwd: appDir })
shouldProcessLuisSsrProjectHtmlFile(
`${appDir}/dist-ssr/docs/index.html`,
config,
)

Match rules:

  • if include is empty, the file is included by default
  • if include is not empty, at least one pattern must match
  • then if exclude matches, the file is rejected
ssr: {
include: ['docs/**', 'blog/**'],
exclude: ['blog/drafts/**'],
}

Supported mini-glob behavior:

  • *
  • **
  • ?

Path normalization always uses /.


  • assetBaseUrl, mode, adapter, and rewritePolicy are carried in the resolved config; the current request-time SSR flow does not branch HTML behavior separately for each of them
  • output.payloadMode is currently only 'inline-json'
  • defaultRuntimeOptions is project-level metadata; the low-level render API does not read it directly
  • include and exclude only decide based on generated HTML relative paths; they do not do source discovery