Emmanuel Akinfulubi

Playground

The real playground is a project on your own machine. The scratchpad underneath runs in this page, compiles your JSX in your browser, and was checked to print exactly what Node printed for the same code when this page was built.

A scratchpad that runs here

Press Edit, change the code, and press Run here. It has the same check() the lessons use, and the rendered result appears in the framed region between the code and the output. Everything is compiled and run in this page: the JSX by the TypeScript compiler that ships with the course, the components by the React bundle in assets/js. Nothing is sent anywhere.

Scratch

ran at build time · in happy-dom 20.11.6 · React dev · runs here too

App.jsx

import { useState, useEffect, act } from 'react';
import { createRoot } from 'react-dom/client';

function Counter() {
  const [n, setN] = useState(0);
  useEffect(() => {
    console.log('effect ' + n);
  }, [n]);
  return <button onClick={() => setN(n + 1)}>clicked {n}</button>;
}

const root = createRoot(stage);
await act(async () => root.render(<Counter />));
console.log(stage.innerHTML);

const button = stage.querySelector('button');
await act(async () => button.click());
console.log(stage.innerHTML);

check('one click, one more', button.textContent, 'clicked 1');
await act(async () => root.unmount());

What node printed (React 19.2.7, development build), when the build ran it

node App.jsx

effect 0
<button>clicked 0</button>
effect 1
<button>clicked 1</button>
ok   one click, one more

What stops a runaway panel

Edited code can be wrong in ways that never finish, so the page has two guards. A while or for loop that spins more than two million times is stopped with a RangeError that says so. And a run that is still rendering after five seconds is ended: every root it created is unmounted, and the output says Stopped after 5 seconds: something kept rendering. At most 1,000 lines, and at most 50 console warnings, are kept, so code that logs on every render cannot flood the page.

One kind of loop is not guarded. An endless chain of promise callbacks, such as (async function f() { await 0; f(); })(), runs in the microtask queue, which runs before the browser gets to the five-second timer, so the timer never fires and the tab stays busy until the browser offers to stop the page. Making every await wait for a timer would fix that and would change the ordering that lesson 26 teaches, so it is not done.

The real playground is a project

On your own machine React lives in a project with a bundler. Vite is the usual choice, and it needs the npm registry, which this course never talks to, so this is shown and not run.

A React project on your own machine

shown, not run
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev

needs Vite and the npm registry, which this offline course never contacts. The scratchpad above is the playground that runs here.

Newsletter

New writing, when it lands.

Occasional notes on shipping software with AI agents. No schedule, no filler, unsubscribe in one click.