Logo for JSON

I decided to learn a bit of React again for some toy projects (my day job JS framework is Angular). Most of the tutorials for setting up a React project "from scratch" ultimately result in using the create-react-app library to hide a lot of the complexity (transpilers, bundlers), but I'm not interested in Webpack or Babel - primarily because I know browsers don't need them anymore. Also, I like to learn about how the bits and bobs work together for small projects like this. Here are the 3 things I want: Typescript, JSX, and compile/deploy to ES modules. Can this actually be done? Yes, and it's not that difficult.

The React ecosystem is huge and there are giant frameworks that then build on top of it. For now, I want to learn a stripped down version of React core called Preact. Most examples out there use JSX not HTM and since I'm using Typescript, I already know I need a build step so fine, let's just bite the JSX bullet!

Here are the minimal HTML, TSX, and JSON files that do something semi-interesting and have zero red squiggles in VS Code and all that other developer goodness.

index.html:

<!DOCTYPE html>
<html>
<body>
  <div id="app"></div>
  <script type="importmap">
    {
      "imports": {
        "preact": "https://esm.sh/preact@10.13.2"
      }
    }
  </script>
  <script src="./dist/index.js" type="module"></script>
</body>
</html>

src/index.tsx:

import { Component, RenderableProps, h, render } from 'preact';

interface AppProps {
  name: string;
}

interface AppState {
  count: number;
}

export class App extends Component<AppProps, AppState> {
  state: AppState = { count: 0 };

  clicky(name: string) {
    const newCount = this.state.count + 1;
    alert(`Hello, ${name}! You clicked ${newCount} times.`);
    this.setState({count: newCount});
  }

  render(props: AppProps) {
    return (
      <div>
        <button onClick={() => this.clicky(props.name)}>
          Clicky
        </button>
        <div>(clicked {this.state.count} times)</div>
      </div>
    );
  }
}

render(<App name="Jeff"/>, document.getElementById("app"));

package.json:

{
  "main": "dist/index.js",
  "type": "module",
  "dependencies": {
    "preact": "^10.13.2",
    "typescript": "^5.0.3"
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "jsx": "react",
    "jsxFactory": "h",
    "lib": [
      "es2022",
      "dom"
    ],
    "module": "es2022",
    "moduleResolution": "nodenext",
    "outDir": "dist",
    "rootDirs": [
      "src"
    ],
    "target": "es2022"
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "src/**/*",
  ]
}

Run npm install to get your (minimal) dependencies installed. Then simply tsc to build (tsc -w if you want it to auto-build every time you save a file). Host a web server in the project directory to play with the thing.

Note the use of an Import Map in the HTML so that you can alias "preact" in your source. This is so that VS Code gets a clue during development, but your deployed source references a minimalized, standalone library that the Preact folks have conveniently provided (for the cost of a tiny 5kb download).

For lack of some acronym to describe this setup, I'll go with TEMP: TSX, Ecmascript Modules, Preact.

§1318 · April 8, 2023 · JavaScript, React, Software, Technology, Web · · [Print]

Leave a Reply