React in an HTML Page

Adding React Directly to an HTML Page

You can run React 7,897 with no build step, which is the fastest way to try an idea or to add one interactive widget to a page your server already renders. Three things have to be in place: React and React DOM as modules, a DOM node to render into, and something that understands JSX.

Older tutorials load react.development.js and react-dom.development.js as UMD scripts and use the globals React and ReactDOM. That recipe stops working at React 19, whose UMD builds were dropped: unpkg.com/react@19.3.0/umd/react.development.js returns 404. The replacement is an import map, which lets the browser resolve a bare specifier such as react to a URL, so the import statements of a real project also work in a single file.

React 19 on a page with no build stepHTMLLive
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <script type="importmap">
    {"imports": {
      "react": "https://esm.sh/react@19.3.0",
      "react-dom/client": "https://esm.sh/react-dom@19.3.0/client"
    }}
  </script>
  <script src="https://cdn.jsdelivr.net/npm/@babel/standalone@8.0.5/babel.min.js"></script>
  <style>body { font: 16px system-ui, sans-serif; margin: 20px } button { font: inherit }</style>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel" data-type="module">
    import React, { useState } from 'react';
    import { createRoot } from 'react-dom/client';
    function LikeButton() {
      const [likes, setLikes] = useState(0);
      return <p>
        <button onClick={() => setLikes(likes + 1)}>Like</button>
        {' '}{likes} {likes === 1 ? 'person likes' : 'people like'} this.
      </p>;
    }
    createRoot(document.getElementById('root')).render(<LikeButton />);
  </script>
</body>
</html>
Browser output of Listing 2.1
Browser output of 1

Click the button and the sentence updates: setLikes schedules a re-render, LikeButton runs again with the new value, and React rewrites one text node. createRoot attaches React to a DOM container and is the only entry point in React 19 — ReactDOM.render was removed.

Two details are easy to miss. type="text/babel" hands the script to Babel 41,624 Standalone instead of the browser, and data-type="module" tells Babel the code contains import statements, so it re-injects the compiled result as a module. Babel Standalone compiles JSX with the classic runtime, turning <p> into React.createElement('p', ...), so React must be imported by name; drop that import and the page throws React is not defined. Build tools use the automatic runtime and need no such import (JSX Syntax and How It Compiles).