No results

    Search failed

    npm GitHub

    Build a React App

    When you want a straightforward client-rendered single-page app โ€” no build-time rendering, no hydration โ€” Poops is just your bundler. One scripts entry points at your JSX/TSX entry, esbuild does the rest.

    Project layout

    react-app/
    โ”œโ”€ poops.json
    โ”œโ”€ package.json          # react + react-dom installed here
    โ””โ”€ src/
       โ”œโ”€ scss/index.scss
       โ”œโ”€ js/
       โ”‚  โ”œโ”€ main.tsx         # entry: mounts the app
       โ”‚  โ”œโ”€ App.tsx
       โ”‚  โ””โ”€ components/
       โ””โ”€ markup/
          โ””โ”€ index.html       # the shell with <div id="root">

    The entry

    // src/js/main.tsx
    import { createRoot } from "react-dom/client";
    import App from "./App";
    
    createRoot(document.getElementById("root")!).render(<App />);
    // src/js/App.tsx
    import { useState } from "react";
    
    export default function App() {
      const [count, setCount] = useState(0);
      return (
        <main>
          <h1>Hello from Poops</h1>
          <button onClick={() => setCount((c) => c + 1)}>Count: {count}</button>
        </main>
      );
    }

    The config

    {
      "scripts": [
        {
          "in": "src/js/main.tsx",
          "out": "dist/js/app.js",
          "options": {
            "minify": true,
            "format": "iife",
            "target": "es2019",
            "jsx": "automatic"
          }
        }
      ],
      "styles": [
        {
          "in": "src/scss/index.scss",
          "out": "dist/css/styles.css",
          "options": { "minify": true }
        }
      ],
      "markup": {
        "in": "src/markup",
        "out": "dist",
        "options": {
          "site": { "title": "My React App" },
          "includePaths": ["_layouts", "_partials"]
        }
      },
      "copy": [{ "in": "src/static", "out": "dist" }],
      "serve": { "port": 4040, "base": "/dist" },
      "livereload": true,
      "watch": ["src"]
    }

    "jsx": "automatic" enables React 17+'s JSX runtime, so you don't import React in every file.

    The HTML shell

    Poops still builds your index.html โ€” use markup for the shell so relativePathPrefix and site data work:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>{{ site.title }}</title>
        <link rel="stylesheet" href="{{ relativePathPrefix }}css/styles.min.css" />
      </head>
      <body>
        <div id="root"></div>
        <script src="{{ relativePathPrefix }}js/app.min.js"></script>
      </body>
    </html>
    

    Note

    React comes from your project's node_modules (npm i react react-dom), not from Poops โ€” see React.

    Develop and build

    poops        # watch + serve + livereload at http://localhost:4040
    poops -b     # one-off production build into dist/

    Tip

    This is a pure SPA: nothing renders until the JS loads. If you want real HTML on first paint (better SEO and perceived speed), pre-render with Reactor instead โ€” see A complete React static site.

    Info

    For a library rather than an app โ€” shipping ESM/CJS/IIFE builds of your components โ€” see Transpiling JavaScript โ€บ Maintaining a JS library.