Skip to content

Scaling

Bundle size and precompile

For hundreds of snippets and a large SDK, success is mostly about how you register, not how much you register.

Register loaders, not values#

tsx
// ❌ always in the page bundle
import * as charts from 'my-charts';
modules={{ 'my-charts': charts }}
// ✅ code-split, fetched when a snippet imports it
modules={{ 'my-charts': defineLoader(() => import('my-charts')) }}

Measured on the playground with a 192 KB vendor module:

RegistrationInitial page JS
by value827 KB
as loader666 KB

A registry of 300 loaders costs nothing. Only specifiers that appear in the compiled snippet are resolved.

What next-live itself costs#

Page importsEntry chunk
LiveProvider + LivePreview + LiveError16.1 KB
above + LiveEditor from next-live/editor97.2 KB

Sucrase is fetched on first compile, or skipped entirely with server precompilation.

Design an SDK surface#

Expose a handful of stable namespaces, not a mirror of your entire codebase:

SpecifierContains
@app/storeApplication state
@app/uiButtons, cards, layout
@app/formatDates, currency
@app/dataFetch helpers scoped to the user

One directory, thin re-exports. Implementations stay free to move.

Generate entries from the filesystem#

tsx
import { registryFromGlob } from 'next-live';
export const generatedModules = registryFromGlob(
import.meta.glob('./modules/*.ts'),
(path) => '@app/' + path.match(/modules\/(\w+)\.ts$/)?.[1],
);

Precompile on the server#

When many users open the same snippet, compile once on the server and ship the result:

tsx
import { precompile } from 'next-live/server';
const result = precompile(source, { filePath: 'app.tsx' });
// Pass result.code to the client via transform={precompiledTransform}

See the precompile tab in the Playground.

Next#

Security: scope 'unsafe-eval' to runner routes only.