Skip to content
This repository was archived by the owner on Oct 12, 2026. It is now read-only.

Add initial SSR support for LiveProvider previews - #1

Open
tujoworker wants to merge 6 commits into
dnbexperience:masterfrom
tujoworker:feat/ssr
Open

tujoworker wants to merge 6 commits into
dnbexperience:masterfrom
tujoworker:feat/ssr

Conversation

@tujoworker

Copy link
Copy Markdown
Member

Summary

Adds server-side rendering (SSR) support for LiveProvider, allowing previews to render during the initial server render pass instead of being empty until client-side hydration.

Changes

  • LiveProvider.tsx: Refactored state initialization so that when useState runs (including on the server), it synchronously transpiles the provided code and produces an element — enabling LivePreview to render meaningful content in renderToString / renderToStaticMarkup.
  • transpile/index.ts: Minor adjustment to support the SSR transpilation path.
  • LiveProvider.ssr.test.js: New test suite covering inline previews, noInline previews, synchronous transformCode, and the async transformCode deferred case.
  • Docs (api.md, usage.md): Updated to document SSR behavior and usage.

Behavior

Scenario SSR output
Inline code Rendered preview
noInline with render() Rendered preview
Synchronous transformCode Rendered preview
Async transformCode Empty preview (deferred to client)

@tujoworker
tujoworker requested a review from langz May 20, 2026 12:25
ryan-roemer and others added 3 commits September 22, 2026 08:43
…bs#418)

Rebuilds the repo tooling and fixes four library bugs. We'll eventually
release as `v5`.

## Library

- `LivePreview` overloads use `React.JSX` instead of the global `JSX`
namespace,
  which `@types/react@19` removed. This is the breaking change.
- Adds an `exports` map. Published filenames are unchanged, so
`main`/`module`/
`types` resolve as before; deep imports into `dist/` no longer resolve.
- Build target `es6` → `es2022`. `engines.node` `>= 0.12.0` →
`>=20.19.0`.
- Forwards `Editor`'s `prism` prop, which was typed but never passed
through.
- Transpiles with sucrase `production`, dropping the `__self`/`__source`
props
  and React 19's outdated-JSX-transform warning.
- Adds `getDerivedStateFromError` to the internal error boundary.

Same exports, same props, same `react >=18` peer range.

## Tooling

- pnpm → npm workspaces, tsup → tsdown (publint + attw on every build),
  jest → vitest, eslint → oxlint, Prettier 3, Docusaurus 2 → 3.
- Removes Storybook and the duplicate `packages/demo`. Stories are now a
plain
Vite app under `packages/react-live/stories`, smoke-tested in Chromium.
- 93 tests across jsdom and Chromium; 93% statement coverage, reported
not gated.
- CI: checks, browser tests, and a React 18 job for the other end of the
peer range.
- Release: changesets v2 actions, npm trusted publishing over OIDC,
approval gate.
…bleLabs#421)

* `live` was `Record<string, unknown>`, so wrapped components saw
`unknown` for every key; it now uses the context's own type. Narrowing,
so: major.
* Stories become `*.stories.tsx` and name their component, typing `args`
as its props, so bad props fail `npm run typecheck`.
* Unrelated: turn off thanks in changesets
# Releases
## react-live@5.0.0

### Major Changes

- [FormidableLabs#418](FormidableLabs#418)
[`be53ccd`](FormidableLabs@be53ccd)
- Support React 19 type definitions, add an `exports` map, drop
`engines`, and raise the
  output target.
  
`LivePreview`'s overloads referenced the global `JSX` namespace, which
`@types/react@19`
removed. Because those types are emitted into the published
declarations, consumers on React
19 types failed to compile. They now use `React.JSX`, which resolves
under both
  `@types/react@18` and `@types/react@19`.
  
The package also gains an `exports` map with separate types for the ESM
and CJS conditions.
Filenames are unchanged from 4.1.x (`dist/index.js`, `dist/index.mjs`,
`dist/index.d.ts`), so
`main`, `module`, and `types` resolve exactly as before. Subpaths other
than
`./package.json` are no longer importable; only the package root was
ever documented.
  
Output is now built for `es2022` rather than `es6`, so the bundle may
contain syntax such as
  optional chaining that previous releases transpiled away.
  
The `engines` field is gone, along with the `node: ">= 0.12.0"` and
`npm: ">= 2.0.0"` it
declared. Both were years out of date, and neither constrained anything
real: this is browser
code, so the Node version that installs it has no bearing on whether it
runs. npm will no
  longer print an engine warning against those bounds.
  
None of this changes the public API -- same exports, same props, same
`react >=18` peer
range -- but the `exports` map and the raised output target are enough
that a major release
  is the honest way to ship them.

- [FormidableLabs#421](FormidableLabs#421)
[`004b007`](FormidableLabs@004b007)
- Type the `live` prop that `withLive` injects, instead of
`Record<string, unknown>`.
  
Wrapped components saw `unknown` for every key, so `live.element` could
not be
rendered and `live.error` could not be read without casting first. The
prop now
  uses the same type the context is created with, which is the shape
[the
docs](https://github.com/FormidableLabs/react-live/blob/main/docs/api.md)
have always described: `code`, `error`, `element`, `onError`,
`onChange`.
  
  ```tsx
  const Panel = withLive(({ live }) => {
    const Result = live.element; // was `unknown`, now a component
    return Result ? <Result /> : <pre>{live.error}</pre>;
  });
  ```
  
Types only, with no runtime change. It is a narrowing, so a wrapped
component
that reads a key outside that shape off `live` will now fail to compile.

### Patch Changes

- [FormidableLabs#418](FormidableLabs#418)
[`be53ccd`](FormidableLabs@be53ccd)
- Forward the `Editor` `prism` prop to `prism-react-renderer`. It was
typed but never passed through, so a custom Prism instance was silently
ignored.

- [FormidableLabs#418](FormidableLabs#418)
[`be53ccd`](FormidableLabs@be53ccd)
- Stop emitting React's `__self` and `__source` debug props from
transpiled code.
  
Sucrase adds these props to every element it compiles. They describe a
source file, and there
is no source file here -- the code comes from a live editor rather than
from disk, so the
filename sucrase emitted was always the empty string. React ignores both
props, and React 19
additionally treats `__self` as the signature of an outdated JSX
transform, logging
"Your app (or one of its dependencies) is using an outdated JSX
transform" to the console of
  every page rendering a `LiveProvider`.
  
Transpiling with sucrase's `production` option drops both props, and
with them the
`_jsxFileName` constant that the transform pipeline used to splice out
and re-add solely to
  keep `__source` resolvable.

- [FormidableLabs#418](FormidableLabs#418)
[`be53ccd`](FormidableLabs@be53ccd)
- Add `getDerivedStateFromError` to the internal error boundary, so a
runtime error in live code no longer logs a React warning about it.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
tunglambk and others added 3 commits October 5, 2026 11:19
…ormidableLabs#416)

### Description

Fixes FormidableLabs#415.

The standalone `Editor` lost DOM focus after the first keystroke, so
only one character could be typed until the user clicked back in.
`use-editable` keys its editing-setup layout effect on the element ref,
which is still `null` during the first render. `LiveProvider` re-renders
when its initial transpile resolves, so that effect settles before the
user types; the standalone `Editor` does not re-render until the first
edit, so the first edit is what makes the effect re-run. The effect's
cleanup resets `contentEditable` (which blurs the element in Chrome) and
its setup then calls `focus()` while the element is still non-editable,
so focus is not restored.

`Editor` now re-renders once after mount, so use-editable's setup
settles before the editor can be edited.

The branch is rebased on master, so the test uses the vitest setup from
FormidableLabs#418: `packages/react-live/src/components/Editor/index.test.jsx`.

#### Type of Change

- [x] Bug fix (non-breaking change which fixes an issue)

### How Has This Been Tested?

Added a regression test in
`packages/react-live/src/components/Editor/index.test.jsx`. It mounts
the standalone `Editor`, drives an edit through use-editable the same
way the browser does (DOM mutation plus keydown/keyup), and asserts the
editing surface is not torn down and rebuilt.

Without the fix it fails: `contentEditable` is written twice
(`undefined`, then `"plaintext-only"`), which is the teardown/rebuild
that resets the surface and drops focus. With the fix no writes occur
and the edit is applied. Running the same scenario against
`<LiveProvider>` + `<LiveEditor>` never writes `contentEditable`,
matching the report that the provider path was unaffected.

The test takes `act` from `@testing-library/react` rather than `react`,
because `React.act` only exists from 18.3.0 and the `React 18.0.0` job
fails with `act is not a function` without it. The library re-exports
`act` for whichever React it is testing against, and the other tests in
this package already import it from there.

jsdom has no contentEditable editing or focus model, so the
browser-level focus loss itself cannot be reproduced in this suite; the
regression test asserts the cause instead. I did not run a real-browser
test.

Validation, all run locally on the rebased branch against the lockfile
React 19.3.0 and against React 18.0.0 (pinned exactly the way the
`react-compat` job does):

- `npm run check` under React 19.3.0 - lint, format, typecheck, and
tests passed (8 files, 58 tests).
- `npm run check` under React 18.0.0 - same result, and the test fails
with `act is not a function` before the import change.
- `npm run build` - library and docs site build passed under both.

### Checklist:

- [x] I have included a changeset if this change will require a version
change to one of the packages.
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have run all builds, tests, and linting and all checks pass
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] My changes generate no new warnings
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to master, this PR
will be updated.


# Releases
## react-live@5.0.1

### Patch Changes

- [FormidableLabs#416](FormidableLabs#416)
[`b8bf7a0`](FormidableLabs@b8bf7a0)
- Fix the standalone `Editor` losing focus after the first keystroke.
`use-editable` rebuilt its editing surface on the first edit's
re-render, which reset `contentEditable` and dropped focus; `Editor` now
settles that surface before it can be edited.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants