Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - jamestitus299/react-code-canvas: A browser-based canvas for rendering React components. · GitHub
Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - jamestitus299/react-code-canvas: A browser-based canvas for rendering React components. · GitHub
Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - jamestitus299/react-code-canvas: A browser-based canvas for rendering React components. · GitHub
Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - jamestitus299/react-code-canvas: A browser-based canvas for rendering React components. · GitHub
Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - jamestitus299/react-code-canvas: A browser-based canvas for rendering React components. · GitHub
Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - jamestitus299/react-code-canvas: A browser-based canvas for rendering React components. · GitHub
Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - jamestitus299/react-code-canvas: A browser-based canvas for rendering React components. · GitHub
Skip to content

Repository files navigation

react-code-canvas

react-code-canvas preview

A browser-based canvas for rendering React components live.

react-code-canvas is useful when you need to preview JSX from strings and for artifacts.

Installation

bun add react-code-canvas@latest
npm install react-code-canvas@latest
yarn add react-code-canvas@latest

Usage

Render a React Component

import{ReactCanvas}from"react-code-canvas";<ReactCanvascode={CODE}showPreview={true}showEditor={false}showError={true}scope={SCOPE}/>;

You can use a Tailwind CDN script in the host app if the rendered code depends on Tailwind utility classes.

Props

PropTypeDescription
codestringReact functional component code, usually export default function..., without import statements.
showPreviewbooleanDisplays the rendered output.
showEditorbooleanShows the code editor.
showErrorbooleanShows an error toast in the top-right of the viewport for runtime or compile-time errors.
errorComponentReact.ReactNode | ((error: string, dismiss: () => void) => React.ReactNode)Replaces the built-in toast. Receives the message and a dismiss callback. Requires showError.
dismissibleErrorbooleanShows a close button on the built-in toast. Defaults to true.
scopeRecord<string, any>Components, values, and libraries available to the rendered code.
persistKeystringOptional localStorage key. When set, edited code is saved on change and restored on reload.
onCodeChange(code: string) => voidCalled whenever the code changes in the editor. Use it to persist code yourself (URL, backend, etc.).
showLoaderbooleanShows a full-screen spinner overlay while the code is being compiled and evaluated. Defaults to !showEditor. Enabling it defers the initial evaluation by one macrotask so the spinner can actually paint; later edits stay synchronous.
loaderReact.ReactNodeReplaces the default spinner with your own node.
onError(error: string) => voidCalled when non-empty code evaluates without throwing but never renders anything.

Validate React Code

Static analysis, no DOM and no React render — safe to run in Node (CI, batch audits, migrations):

import{analyzeReactCode}from"react-code-canvas";constresult=awaitanalyzeReactCode(CODE);// {// valid: boolean,// issues: [{ type, message, name?, line?, column? }],// unknownGlobals: string[], // names that will throw ReferenceError// referencedGlobals: string[],// hasDefaultExport: boolean,// }

It parses, transpiles with sucrase, and compiles with new Function (constructed, never called), then checks every referenced name against the scope. It does not execute the code, so it cannot catch logic errors or failures that only surface once mounted — render it with ReactCanvas and its onError for that.

Options

OptionTypeDescription
scopeRecord<string, unknown>Omit to check against exactly what the canvas would load for this code. Passing one replaces that entirely.
allowedGlobalsstring[]Extra host-injected names to treat as available.
forbidImportsbooleanReport import statements. Default true — the canvas strips them, so the bindings are undefined at runtime.

Editable React Code Canvas

import{EditTextReactCanvas}from"react-code-canvas";<EditTextReactCanvascode={CODE}scope={SCOPE}showPreview={true}showEditor={false}showError={true}onSaveFinalCode={handleSave}onError={handleError}/>;

EditTextReactCanvas adds text editing behavior to rendered React code (beta).

Build a standalone website

Turn a code string into a deployable static site — index.html plus hashed assets/, with React, every library the code references and compiled Tailwind bundled in. No CDN, no network at runtime.

// Node only. Ships as a separate entry so nothing here reaches your browser bundle.import{writeStandaloneSite}from"react-code-canvas/builder";awaitwriteStandaloneSite(CODE,"./out");// -> out/index.html, out/assets/app-<hash>.js, out/assets/style-<hash>.css

Then deploy out/ anywhere that serves files. Use buildStandaloneSite instead to get a path -> contents map and handle delivery yourself.

Requires the optional peers it actually uses: npm i -D esbuild, plus tailwindcss if your code uses className. Neither is loaded otherwise.

Options

OptionTypeDescription
titlestring<title> for code that emits none of its own. A <Helmet><title> in the code wins.
langstring<html lang>. Default 'en'.
tailwindbooleanCompile Tailwind for the classes found in the code. Default true; only runs when the code contains className.
cssstringExtra CSS, appended after the compiled Tailwind.
headstringRaw HTML appended to <head> — fonts, favicon, OG tags, analytics.
prerenderbooleanBake the initial markup into the HTML for SEO, then hydrate. Default true.
minifybooleanDefault true.
targetstringesbuild target. Default 'es2020'.

Notes:

  • Styling all works: inline style objects, in-component <style> tags, and Tailwind classes are compiled into a real stylesheet. Unlike the canvas, the built site does not need the host to provide Tailwind.
  • Prerendering is best-effort. Code that touches document or windowduring render throws in Node; the build warns and emits a client-only shell instead of failing. Guard with typeof document !== "undefined" to keep the prerender. useEffect never runs during prerender, so it is always safe.
  • <Helmet> finally does something here — React 19 hoists its <title>/<meta> and the builder lifts them into <head>, where crawlers read them.
  • Machine-generated <EditableText> tags from an EditTextReactCanvas session are reversed back to plain JSX; a deployed page does not keep click-to-edit fields.
  • recharts charts do not prerender — measured: they render nothing server-side, fixed size or not, and appear on hydration. The rest of the page still prerenders normally, so only the chart area is blank in the initial HTML.

Notes

  • Code should export a default component, for example export default function ComponentName() {}.
  • Do not include import statements in rendered code. Inject dependencies through the scope prop.
  • The default scope ships React, its hooks and react-helmet-async up front, and lazily fetches lucide-react (~0.8KB gz per icon), recharts (~145KB gz), motion/react (~61KB gz) and react-icons/fa (~420KB gz) only when the code mentions a name from that group. Prefer lucide icons — one Fa* reference pulls the entire Font Awesome pack, which has no per-icon files. See Scope.
  • Empty or whitespace-only code renders nothing at all — no output and no loader.
  • The built-in error toast keeps the message in #react-code-error. If you replace it with errorComponent and rely on scraping that element (for example from a headless browser), keep the id on a statically positioned element — offsetParent is null on position: fixed elements.
  • errorComponent takes an element or a render function, not a component type. Passing the component itself (errorComponent={MyToast}) calls it with the message string in place of props, so it renders blank with no warning. Use errorComponent={(message, dismiss) => <MyToast message={message} onDismiss={dismiss} />} — the second argument wires your own close control to the same per-message dismissal the built-in toast uses.
  • When an edit fails to compile, ReactCanvas keeps the previous successful render on screen and reports the error. Doing so re-executes the previous code, so any top-level side effects in it (analytics calls, script injection, DOM mutation) run again. Guard side effects if that matters.
  • This package evaluates provided code in the browser. Do not execute untrusted code without an additional isolation strategy appropriate for your application.

Development

This repository uses Bun for dependency management and CI.

bun install --frozen-lockfile
bun run test
bun run build

Run Storybook locally:

bun run dev

Other useful commands:

bun run build-storybook
bun run size

The committed lockfile is bun.lock. Do not commit package-lock.json.

Documentation Site

The package documentation lives in doc_site and is deployed to GitHub Pages from the master branch.

bun run docs:dev
bun run docs:build

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.

All participants are expected to follow the Code of Conduct.

Changelog

See CHANGELOG.md for version updates.

License

MIT. See LICENSE.

About

A browser-based canvas for rendering React components.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages