Skip to content

API reference

Every export and prop

Every export is a named binding, never a property on a parent object. Across the RSC boundary a Server Component receives a client reference, so Live.Preview would resolve to undefined.

Three entry points
import { LiveProvider, LivePreview, defineLoader } from 'next-live';
import { LiveEditor } from 'next-live/editor'; // optional peer: prism-react-renderer
import { precompile, validateSnippets } from 'next-live/server'; // server only
EntryContainsWhy it is separate
next-liveProvider, preview, error, hooks, registry, engine-
next-live/editor<LiveEditor>The only thing needing prism-react-renderer, an optional peer you must install yourself. A preview-only page pays 16.1 KB instead of 97.2 KB.
next-live/serverprecompile, validateSnippet(s)Imports Sucrase statically, must never reach the client bundle.

Components#

LiveProvider

componentfrom next-live
function LiveProvider(props: LiveProviderProps): ReactNode

Compiles code and provides the result to its children. Everything else must be inside it. Safe to render from a Server Component; nothing compiles during the server pass.

  • coderequired

    Type
    string
    Default
    -

    The snippet. Controlled: change it and the preview follows.

  • modules

    Type
    ModuleRegistry
    Default
    {}

    Specifier → value or loader. Merges over the built-ins.

  • scope

    Type
    LiveScope
    Default
    {}

    Free variables injected as bare identifiers without import.

  • props

    Type
    Record<string, unknown>
    Default
    {}

    Passed to the rendered component by reference.

  • fallback

    Type
    ReactNode
    Default
    null

    Rendered until the first compile finishes. Size it like your content to avoid layout shift.

  • language

    Type
    string
    Default
    'tsx'

    Highlighting hint for <LiveEditor>.

  • onError

    Type
    (error: Error) => void
    Default
    -

    Called on every compile and runtime error.

  • onCodeChange

    Type
    (code: string) => void
    Default
    -

    Called when the code is edited from inside. Not called when the code prop changes from outside, so it cannot echo your own saves back. This is what a control panel needs to persist edits.

  • onCompileSuccess

    Type
    (info: CompileSuccessInfo) => void
    Default
    -

    After every successful compile, with compileId, sorted imports, via, and durationMs. Not called on failure or abort.

  • debounce

    Type
    number
    Default
    150

    Milliseconds before recompiling after a change.

  • keepLastGood

    Type
    boolean
    Default
    true

    Keep the last working component mounted when a recompile fails.

  • maxRendersPerSecond

    Type
    number
    Default
    1000

    Render-loop breaker threshold.

  • transform

    Type
    TransformFn
    Default
    -

    Replace the built-in Sucrase pass, e.g. server-precompiled output.

  • resolveSubpaths

    Type
    boolean
    Default
    false

    Resolve pkg/Sub against a registered pkg by property access.

  • filePath

    Type
    string
    Default
    'LiveCode.tsx'

    Name shown in stack traces and DevTools.

  • production

    Type
    boolean
    Default
    true

    false selects react/jsx-dev-runtime for richer component stacks.

  • jsxRuntime

    Type
    'automatic' | 'classic'
    Default
    'automatic'
  • jsxImportSource

    Type
    string
    Default
    'react'

    Register <source>/jsx-runtime if you change this.

Preview

Source sent to LiveProvider

Anything in props is handed to the rendered component by reference, this is how a snippet reads host state it must not be able to import:

Preview

Source sent to LiveProvider

LivePreview

componentfrom next-live
function LivePreview(props: LivePreviewProps): ReactNode

Renders the compiled component inside an error boundary.

  • props

    Type
    Record<string, unknown>
    Default
    -

    Merged over the provider's props.

  • fallback

    Type
    ReactNode
    Default
    provider's

    Shown until the first compile finishes.

  • as

    Type
    ElementType
    Default
    'div'

    Wrapper element.

  • className / style

    Type
    -
    Default
    -

    Applied to the wrapper.

LiveEditor

componentfrom next-live/editor
function LiveEditor(props: LiveEditorProps): ReactNode

A <textarea> layered over syntax-highlighted output. No editor engine, so it stays small and has no SSR quirks. On its own entry point because it is the only thing that pulls in prism-react-renderer.

  • renderEditor

    Type
    (props: LiveEditorRenderProps) => ReactNode
    Default
    -

    Replace the built-in editor entirely, drop in CodeMirror or Monaco.

  • theme

    Type
    PrismTheme
    Default
    themes.vsDark

    From prism-react-renderer.

  • prism

    Type
    typeof Prism
    Default
    built-in

    A Prism instance with extra languages registered, for anything outside the built-in set.

  • readOnly

    Type
    boolean
    Default
    inferred

    Display source without editing. Defaults to true when there is nothing to write edits to.

  • tabSize

    Type
    number
    Default
    2

    Spaces inserted by the Tab key.

  • padding

    Type
    number
    Default
    16
  • errorLineStyle

    Type
    CSSProperties | null
    Default
    red inset highlight

    Paint-only highlight on the error line. Pass null to disable.

  • errorLineClassName

    Type
    string
    Default
    -

    Extra class on the error line.

  • focusRingStyle

    Type
    CSSProperties | null
    Default
    2px blue outline

    Ring painted while the editor holds keyboard focus. Pass null only if you paint your own.

  • aria-label

    Type
    string
    Default
    'Live code editor'

    Accessible name.

  • code

    Type
    string
    Default
    from context

    Standalone mode, see below.

  • onChange

    Type
    (code: string) => void
    Default
    from context

    Standalone mode, see below.

  • language

    Type
    string
    Default
    from context, then 'tsx'
  • error

    Type
    Error | null
    Default
    from context

    Error to underline.

Standalone, without a provider#

<LiveEditor> normally takes its code from the surrounding <LiveProvider> and sends edits back to it. Pass code and it works on its own, which is how a page shows a highlighted snippet it is not running:

A highlighted snippet with no provider
// Read-only: no onChange, so there is nowhere to write an edit.
<LiveEditor code={source} language="tsx" />
// Editable, driven by your own state.
<LiveEditor code={code} onChange={setCode} />

With neither a provider nor code, the editor throws rather than rendering empty.

LiveEditorRenderProps exposes code, onChange, language, error, errorLine, and errorColumn, everything a third-party editor needs:

Swapping in a different editor
<LiveEditor
renderEditor={({ code, onChange, language, errorLine }) => (
<CodeMirror value={code} onChange={onChange} lang={language} highlightLine={errorLine} />
)}
/>

Preview

Source (editable)

Tab inserts spaces. Press Escape and then Tab to move focus out of the editor.

LiveError

componentfrom next-live
function LiveError(props: LiveErrorProps): ReactNode

Shows the current compile or runtime error; renders nothing when healthy. Both phases surface here, so it is a single place to look.

  • children

    Type
    (error: Error) => ReactNode
    Default
    built-in rendering

    Custom error rendering.

  • as

    Type
    ElementType
    Default
    'pre'
  • className / style

    Type
    -
    Default
    -

LiveErrorBoundary

componentfrom next-live
function LiveErrorBoundary(props: LiveErrorBoundaryProps): ReactNode

Used internally by <LivePreview>. Exported for custom UIs that render the compiled component themselves.

  • onErrorrequired

    Type
    (error: Error) => void
    Default
    -

    Called when rendering the snippet throws.

  • resetKey

    Type
    unknown
    Default
    -

    Changing it clears the error. Wire this to compileId.

  • fallback

    Type
    ReactNode
    Default
    -

    Rendered while in the error state.

Hooks#

Snippets share your application's single React instance, so hooks, effects, and context all work across the boundary. The timer below is running useState and useEffect from your React:

Preview

Source sent to LiveProvider

useLiveRunner

hookfrom next-live
function useLiveRunner(options: UseLiveRunnerOptions): LiveRunnerState

The headless engine behind <LiveProvider>, for building a completely custom UI. Accepts every LiveProvider option except props, language, onError, and fallback.

tsx
const { code, setCode, Component, element, error, isCompiling, compileId } =
useLiveRunner({ code: source, modules });
  • code

    Type
    string
    Default
    -

    Current source.

  • setCode

    Type
    (code: string) => void
    Default
    -

    Stable identity.

  • Component

    Type
    ComponentType | null
    Default
    -

    null until the first successful compile, including during SSR.

  • element

    Type
    ReactElement | null
    Default
    -

    Set instead of Component when the snippet produced an element.

  • error

    Type
    Error | null
    Default
    -
  • isCompiling

    Type
    boolean
    Default
    -

    Only true after ~200 ms, so fast compiles never flash a spinner.

  • compileId

    Type
    number
    Default
    -

    Increments on every successful compile. Use as a remount key.

useLiveModule

hookfrom next-live
function useLiveModule<T>(options: UseLiveModuleOptions): LiveModuleState<T>

Runs a snippet and returns its exports, for stored code that is not a component

  • a validator, a transformer, a calculated field. Accepts every useLiveRunner option except maxRendersPerSecond (nothing renders, so there is no render loop to break).
  • exports

    Type
    T | null
    Default
    -

    Everything the snippet exported. null until the first successful run.

  • value

    Type
    unknown
    Default
    -

    Shorthand for exports?.default.

  • error

    Type
    Error | null
    Default
    -
  • isCompiling

    Type
    boolean
    Default
    -
  • compileId

    Type
    number
    Default
    -

useLiveModule: non-UI snippet

useLiveContext

hookfrom next-live
function useLiveContext(): LiveContextValue

Reads the surrounding <LiveProvider>, for custom editors, toolbars, or status indicators. Throws if called outside a provider. Returns every useLiveRunner field plus props, language, fallback, and reportRuntimeError.

A compile-status indicator
'use client';
import { useLiveContext } from 'next-live';
export function CompileStatus() {
const { isCompiling, error, compileId } = useLiveContext();
if (isCompiling) return <span>Compiling…</span>;
if (error) return <span className="text-destructive">Failed</span>;
return <span>Compiled #{compileId}</span>;
}

Registry helpers#

defineLoader

functionfrom next-live
function defineLoader(load: ModuleLoader): ModuleLoader

Marks a function as a lazy loader rather than as the module value itself. Without it, a registered component function would be indistinguishable from a loader. This is the default way to register a module; it keeps your own dependencies out of the page bundle.

The loader receives the imported specifier, which is what prefix entries need.

tsx
// Exact match, the specifier can be ignored.
'@app/store': defineLoader(() => import('@/lib/store')),
// Prefix entry (key ends in '/'), one loader serves the whole subtree.
'big-lib/': defineLoader((specifier) => import(`big-lib/${specifier.slice(8)}`)),

defineModule

functionfrom next-live
function defineModule(shape: { default?: unknown; exports?: Record<string, unknown> }): NormalizedModule

Builds an explicit module record. A registered object with its own default key is normally unwrapped; use this when the whole object is the default export. default and exports.default address the same slot.

createRegistry

functionfrom next-live
function createRegistry(...groups: ModuleRegistry[]): ModuleRegistry

Merges registry groups, later groups winning. Warns in development when two groups define the same key, which is how you catch a silent override.

registryFromGlob

functionfrom next-live
function registryFromGlob(glob: GlobResult, toSpecifier: (path: string) => string | null): ModuleRegistry

Converts import.meta.glob's lazy result into a registry of loaders. toSpecifier maps a file path to the specifier authors write; return null to omit a file.

builtinModules

constantfrom next-live
const builtinModules: ModuleRegistry

The always-registered map: react, react/jsx-runtime, and react/jsx-dev-runtime. Your modules merge over these, so a React shim can still be substituted. react-dom is deliberately absent, register it explicitly if you need it.

Experimental APIs#

These exports are marked @experimental in source. They may change in minor releases without a major bump. Prefer the stable engine surface below for production integrations.

ExportPurpose
setTranspiler(module)Swap the transpiler implementation. Intended for tests and custom backends.
createRenderBudget(options)Configure the render-loop breaker used during evaluation.

Engine#

Lower-level exports, for a custom scheduler or for compiling outside React.

compile

functionfrom next-live
function compile(input: CompileInput): Promise<CompileResult>

Transpile, resolve every import, then evaluate. Returns { renderable, via, code, imports }. Throws if the snippet produced nothing renderable.

compileModule

functionfrom next-live
function compileModule(input: CompileInput): Promise<CompileModuleResult>

The same pipeline, returning { exports, code, imports } with no component required.

transpile

functionfrom next-live
function transpile(source: string, options: TranspileOptions, transform?: TransformFn): Promise<TransformResult>

Source → CommonJS. No evaluation, no module resolution.

preloadTranspiler

functionfrom next-live
function preloadTranspiler(): Promise<void>

Warms the lazily-fetched Sucrase chunk during idle time, so the first compile does not pay for the download. Call it when you know an editor is about to open.

precompiledTransform

functionfrom next-live
function precompiledTransform(result: TransformResult): TransformFn

Wraps a server-precompiled result as a transform, so the client never loads Sucrase at all. Exported from the client entry, importing it from next-live/server would pull the transpiler into your page.

setTranspiler

functionfrom next-live
function setTranspiler(module: unknown): void

Swaps the transpiler outright. For tests and custom backends.

errorPosition

functionfrom next-live
function errorPosition(error: Error | null | undefined): { line: number; column?: number } | null

Reads { line, column? } off a compile or runtime error, and returns null when there is no position. This is what a custom editor uses to paint the failing line. Pass it the error from useLiveRunner or useLiveContext directly; a null error is handled.

createRenderBudget

functionfrom next-live
function createRenderBudget(options?: RenderBudgetOptions): () => void

The render-loop breaker. A fresh budget is created per compile, so fixing a runaway snippet clears a tripped breaker without a page reload.

normalizeModule

functionfrom next-live
function normalizeModule(value: ModuleValue): NormalizedModule

The interop normalisation applied to every registry value: maps an arbitrary object, function, or namespace onto default plus named exports.

createRequire

functionfrom next-live
function createRequire(resolved: ResolvedModules): (specifier: string) => NormalizedModule

The synchronous require shim handed to compiled snippets. It must stay synchronous: Sucrase emits require() at module top level, which cannot await.

resolveModules

functionfrom next-live
function resolveModules(options: ResolveOptions): Promise<ResolvedModules>

Resolves every specifier a snippet needs before evaluation, which is why async loaders work at all despite require() being synchronous.

Server entry#

precompile

functionfrom next-live/server
function precompile(source: string, options?: TranspileOptions): PrecompileResult

Transpiles to exactly the same CommonJS the browser path produces, so a precompiled result and a client-compiled one are interchangeable. Transpile once, cache by hash, and the client never downloads the transpiler.

  • code

    Type
    string
    Default
    -

    The transpiled CommonJS.

  • hash

    Type
    string
    Default
    -

    Stable hash of the source and the options that affect output. Use it as a cache key or ETag.

  • linePrefixOffset

    Type
    number
    Default
    -

    Lines the wrapper added above the snippet; needed to map error lines back.

  • expression

    Type
    boolean
    Default
    -

    Whether the snippet compiled as a bare expression rather than a module.

PrecompileResult extends TransformResult, so a result hands straight to precompiledTransform.

validateSnippet

functionfrom next-live/server
function validateSnippet(source: string, options?: ValidateOptions): ValidationResult

Statically checks that a snippet compiles and that every import resolves. Never evaluates, so it is safe to run over untrusted content in CI. modules accepts a registry object or just its keys.

tsx
const result = validateSnippet(source, { modules: ['@app/store', 'big-lib/'] });
// { ok, issues: [{ kind, message, specifier?, suggestion?, line?, column? }], imports }
  • maxSourceBytes

    Type
    number
    Default
    -

    Reject snippets over this UTF-8 byte count before transpile.

  • forbidNodeBuiltins

    Type
    boolean
    Default
    -

    Treat node:* imports as forbidden.

  • forbidRemoteImports

    Type
    boolean
    Default
    -

    Treat https://, http://, and // imports as forbidden.

  • denySpecifiers

    Type
    readonly string[]
    Default
    -

    Deny listed specifiers even when registered. A trailing / denies a subtree.

All policy flags are opt-in; defaults are unchanged.

validateSnippets

functionfrom next-live/server
function validateSnippets(snippets, options?): { id: string; result: ValidationResult }[]

Validates many at once and returns only the failures, so a CI step can assert on an empty array. Each snippet is { id, source }.

Errors#

All extend LiveError, exported as LiveErrorBase so it does not collide with the <LiveError> component.

ClassRaised when
LiveCompileErrorParse/transpile failure, or a CSP blocking eval. Carries line and column.
LiveRuntimeErrorThe snippet threw. Carries line where it can be mapped.
RenderLoopErrorThe render-rate breaker tripped.
ModuleNotFoundErrorAn import specifier is not registered. Carries specifier and available.
NoComponentErrorThe snippet produced nothing renderable.
TranspilerLoadErrorSucrase failed to load, usually a chunk-load failure.

A snippet that throws while rendering is caught by the boundary inside <LivePreview> and surfaced through <LiveError>; the page itself never goes down:

Types#

ModuleRegistry, ModuleLoader, ModuleValue, NormalizedModule, LiveScope, LiveRenderable, LiveRunnerState, LiveContextValue, CompileOptions, CompileResult, CompileInput, CompileModuleResult, CompileSuccessInfo, TranspileOptions, TransformFn, TransformResult, ExtractionSource, UseLiveRunnerOptions, UseLiveModuleOptions, LiveModuleState, RenderBudgetOptions, GlobResult, PositionedError, PrecompileResult, ValidationResult, ValidationIssue, ValidationIssueKind (syntax, unresolved-import, source-too-large, forbidden-import), ValidateOptions, plus a props type for every component, LiveProviderProps, LivePreviewProps, LiveEditorProps, LiveEditorRenderProps, LiveErrorProps, LiveErrorBoundaryProps.