react-playground.landing Open editor

react/playground

React Playground for Fast Prototyping and Component Testing

Answer a small React question in the time it takes to ask it — hooks running against the real runtime, a console beside the app, and a link you can hand to whoever is waiting on the answer.

loophot update
runtimereal React 19
consolebeside the app
outputshareable link
Playground.jsx
import { useState, useEffect } from 'react'

export default function Counter() {
  const [n, setN] = useState(0)
  useEffect(() => console.log('n is', n), [n])

  return <button onClick={() => setN(n + 1)}>{n}</button>
}
hot update

01 platform

Why hooks are best understood by running them

React's hard parts are timing questions — when an effect fires, why a value is stale, what Strict Mode is doing. Reading about them is slower than watching them.

  • Strict Mode double-invocation, visible

    React 19 deliberately runs effects twice in development to surface missing cleanup. Watching that happen in a console beside the app explains it in ten seconds; reading about it rarely does.

  • The rules of hooks, enforced by React itself

    Call a hook conditionally and you get React's own error, not a lint warning about it. The rule and its consequence arrive together, which is how it becomes intuition.

  • Stale closures where you can see them

    The effect that captured an old value is React's most-searched bug. It reproduces here in three lines, and the fix reproduces just as fast.

  • Reconciliation you can provoke

    Change a key, watch a subtree remount and its state vanish. Provoking that once teaches more about React's identity model than a paragraph describing it.

02 pipeline

Testing a hook, start to finish

Four moves. This is what it costs to test React components here, against the fifteen minutes a local scaffold costs before you write the first line.

  1. Write the hook

    A custom hook in its own file, imported by a component that renders its output. Two files, because a hook that only exists inside one component is not the thing you are testing.

  2. Log the boundary

    A console.log at the top of the hook and one inside its effect. Render count and effect count are different numbers, and the gap between them is where most hook bugs live.

  3. Provoke it

    Change a dependency, toggle the component, resize the window. Each interaction prints its trace beside the running app, so cause and effect stay adjacent.

  4. Share the trace

    Send the link. Whoever opens it gets the same hook, the same logs and the same reproduction, which is what an issue thread actually needs.

03 features

What the playground gives a React experiment

Everything below matters specifically for testing React components and hooks, rather than for building an application. It is a React REPL with the parts a real experiment needs.

  • React 19 dev build

    Dev-mode warnings are on: dependency arrays, missing keys, hook order, Strict Mode double-invocation. These are the messages that teach, and a production build hides all of them.

  • Fast Refresh semantics

    Editing a component's body preserves its state; changing its identity or hook order remounts it. That is React's own rule, honoured rather than approximated.

  • Console adjacent to the render

    Render logs, effect logs and warnings interleave in the order React actually produced them, which is the information a separate devtools panel makes you reconstruct.

  • Real libraries, real behavior

    Install React Router, Zustand or TanStack Query and test against the actual implementation. A mock answers a question you did not ask.

  • TSX with no type-check pause

    Rename to .tsx and annotations strip on the same path JSX takes. Testing a generic component costs nothing extra.

  • A link that reproduces

    Share the project and it opens as you left it — the reason this is usable on a GitHub issue rather than as a screenshot.

04 deep dive

Watching a hook actually fire

Most playground questions are really one question: when does this re-run? A loop this short answers it by observation rather than by reading the docs again.

useEffect dependencies are observable. Log inside the effect and the console tells you exactly which change triggered it — including the ones you did not expect, which are the useful ones.

Stale closures become visible. The classic bug where an effect captures an old value is a five-second experiment here instead of a twenty-minute debugging session later.

useMemo caching can be watched. Put a log inside it and render twice; it fires once. That is a thing you can be told, and a thing you can watch, and the second one sticks.

Cleanup runs where you can see it. The function an effect returns fires on unmount, which is easiest to believe when you have toggled a component and watched the log.

useEffect
dependency tracking
useMemo
cached, observable
cleanup
on unmount
console
beside the preview
Effects.jsx
const [q, setQ] = useState('')

useEffect(() => {
  console.log('effect ran for', q)
  return () => console.log('cleanup for', q)
}, [q])

05 workflow

Watching a hook misbehave, then fixing it

One real sequence, from opening the tab to having an answer worth sharing. The console sits beside the app, so nothing here required opening devtools.

session
  • open → React playground
  • App.jsx rendered · 34ms
  • [log] effect ran for ""
  • edit → omit dependency
  • React Hook useEffect has a missing dependency: 'q'
  • TypeError: Cannot read properties of undefined
  • at SearchBox (SearchBox.jsx:22)
  • fix → add dependency → save
  • re-rendered · 0 errors · 0 warnings
  • console.log lands in view

    Logs, warnings and errors appear next to the running app rather than in a panel you have to remember to open.

  • Throws name the component

    React's component stack reports the component and the line, so the search starts in the right file rather than in the whole project.

  • Warnings arrive while you are still looking

    React's dev-mode warnings — dependency arrays, key props, hook order — surface at the moment the mistake compiles, not after you have moved on.

  • Recovery is one save

    Fix the line and save. The tree reconciles in place; there is no process to restart and no server to wait for.

06 deep dive

Debugging a render that throws

Running code is only half of it. The half that decides whether an hour is productive is what happens in the seconds after something breaks.

The error is attributed. A throw carries React's component stack and the line in your source, because a trace through the reconciler helps nobody.

The previous render survives. A failed transform leaves the last working output on screen with the error beside it, so you still have the thing you were comparing against.

Your browser's debugger works. Breakpoints, stepping and the call stack all apply, because this is ordinary JavaScript executing in an ordinary browser rather than a sandboxed interpreter with its own rules.

errors
component stack
warnings
React dev mode
breakpoints
browser devtools
restart
never needed
SearchBox.jsx
export default function SearchBox({ results }) {
  return (
    <ul>
      {results.map(r => <li key={r.id}>{r.title}</li>)}
    </ul>
  )
}

07 ecosystem

One project, many stacks

A .jsx file can sit beside a .vue one and a Python script; the compiler is chosen per file, not per project.

  • React
  • Vue
  • Angular
  • Svelte
  • SolidJS
  • Lit
  • HTMX
  • Vite
  • TypeScript
  • JavaScript
  • HTML
  • CSS
  • SCSS
  • Less
  • Pug
  • Tailwind
  • Bootstrap
  • Vuetify
  • Ant Design
  • shadcn/ui
  • Phaser
  • Node.js
  • Python
  • Java
  • C++
  • C#
  • Go
  • Rust
  • Ruby
  • PHP
  • Kotlin
  • Swift
  • SQL
  • Bash

08 languages

Everything the editor understands

Highlighting and IntelliSense cover 70+ languages. These are the ones XCODX also compiles or executes for you.

Compiled in your browser11

Zero build step. Transform runs on a Web Worker, so typing never blocks.

  • JSX
  • TSX
  • TypeScript
  • Vue SFC
  • Svelte
  • Angular (JIT)
  • SolidJS
  • SCSS
  • Sass
  • Less
  • Pug

Rendered natively8

Straight to the live preview, no transform in the way.

  • HTML
  • CSS
  • JavaScript
  • JSON
  • Markdown
  • Jupyter (.ipynb)
  • SVG
  • YAML

Executed on demand22

70+ languages run through a self-hosted Piston engine. stdout, stderr, images and tracebacks land in the console.

  • Python
  • Java
  • C
  • C++
  • C#
  • Go
  • Rust
  • Ruby
  • PHP
  • Kotlin
  • Swift
  • Lua
  • Scala
  • Perl
  • Haskell
  • Elixir
  • Dart
  • R
  • Julia
  • SQLite
  • Bash
  • PowerShell

09 questions

Using the React playground — common questions

Questions about the fast loop specifically.

Why does my effect run twice?

Strict Mode. React 19 intentionally mounts, unmounts and remounts components in development to surface effects that do not clean up after themselves. It is not a bug and it does not happen in production — and watching it here is the fastest way to understand what it is checking for.

Which React version do hooks run against?

React 19, mounted with createRoot, in its development build. Actions, useActionState, useOptimistic and use are all available, and the dev-mode warnings are React's own rather than an approximation of them.

Does component state survive an edit?

Editing a component's body preserves state, the way Fast Refresh does locally. Changing its identity, its hook order or its key remounts it — because showing you state that no longer matches your code would be worse than losing it.

Can I see console output without opening devtools?

Yes. Render logs, effect logs and React's warnings appear beside the running app, interleaved in the order React produced them. Devtools still work if you prefer them.

Can I test a custom hook across files?

Yes, and you should. A hook that only exists inside one component is not really a hook. Put it in its own file, import it from a component, and you are testing the thing you will actually ship.

Does React DevTools work here?

The browser extension attaches to the preview like it would to any React app, because the preview is a real React tree rather than a rendered picture of one. The component inspector and profiler both work.

Can I reproduce a bug for a GitHub issue?

Yes, and multi-file reproduces a class of bug a single file cannot — anything crossing a custom hook, a context provider or a router. The link opens exactly what you ran, which is what a maintainer needs.

Do npm packages really install?

Yes. The package resolves through esm.sh and imports by name via a native import map, so you are testing against the library rather than a stand-in for it.

Is there a way to test the production build?

Not here — the dev build is the point, because its warnings are what make the loop useful. A production build belongs where you can measure it, which is a real build pipeline rather than a browser tab.

Do I need to install anything to use XCODX Studio?

No. XCODX Studio is a browser-based IDE — the editor, the compilers and the preview all run client-side. There is no download, no package manager to set up and no configuration file to write. Opening the editor is the whole setup step.

Where is my code stored?

In your own browser. Projects, file history and Git objects live in local storage on your device and are never uploaded to a server. That also means XCODX works offline: it is a Progressive Web App, so you can install it and keep coding with no connection.

Is XCODX Studio free?

Yes. The editor, the live preview, the 70+ language runtimes, npm installs, Git and the integrated terminal are all free to use.

Can I use XCODX on a tablet or Chromebook?

Yes. The editor is a responsive web app, so a Chromebook, an iPad or an Android tablet gets the same compilers and the same preview a laptop does — there is no desktop-only build, because there is no build to install at all. The layout adapts to the screen rather than shrinking a desktop UI into it.

Can I export a project or move it to my own machine?

Yes. Export the whole project as a zip with its folder structure intact and open it locally, or commit it and push. It is ordinary source on disk from that point on — no proprietary format and no lock-in, which is also why importing an existing repository from GitHub works the same way in reverse.