TypeScript
Summary
Bare minimum required for sharing a React component written in TypeScript.
Nobody wants to write React without JSX. As we're going to need a build step anyway and there's no sane reason to build something without TypeScript nowadays, we're going to go directly to JSX + TypeScript.
Starting from the bare example, we
- move the 
index.jstosrc/index.tsx(to better separate code and build artefacts later), and - add 
typescriptand React's types to ourdevDependencies(see TODO for an overview over the different dependency types):pnpm add -D typescript @types/react. 
Once we set up a build step, the built library will be exposed in dist/index.js together with a declaration file at dist/index.d.ts, so we update the package.json accordingly:
{
  "main": "dist/index.js",
  "types": "dist/index.d.ts"
}Setting up TypeScript
In order to compile TypeScript + JSX to vanilla JavaScript, we will add a build script to our package.json that runs the TypeScript compiler (we will use fancier tooling like esbuild in the future):
{
  "scripts": {
    "build": "tsc"
  }
}By default, the TypeScript compiler doesn't really know what to do with your stuff, so you need to create a tsconfig.json:
{
  "include": ["src"],
  "compilerOptions": {
    "target": "ESNext",
    "moduleResolution": "nodenext",
    "outDir": "dist",
    "jsx": "react-jsx"
  }
}include(Reference): This tells TypeScript which files to look at.compilerOptions.target(Reference): This tells TypeScript which version of ECMAScript (aka which JavaScript standard) to compile to. When developing apps, this should be set to something sensible likeES6. For libraries like here, we want the consuming application to have full control over its own bundling and polyfilling, so we use the most up-to-date standard, which is exposed asESNext.compilerOptions.moduleResolution(Reference): There's three choices here,classic,nodeandnodenext(akanode16). You probably never want to useclassicin modern projects.nodereferences Node's CommonJS resolution algorithm. Since we want to emit an ES Module, we will usenodenext, Node's ES Modules resolution algorithm.compilerOptions.outDir(Reference): This tells TypeScript where to put the compiled files. We want them indist(don't forget to add that directory to your.gitignore!)compilerOptions.jsx(Reference): This informs the TypeScript compiler that we will use JSX syntax. TypeScript can convert that either tocreateElementcalls throughreact(which we used in Level 1) or newer_jsxcalls available since React 17 throughreact-jsx(see this blog post). We want the modern stuff and will usereact-jsx.
The library can now be published and consumed analogous to the library from Level 1. However, VSCode won't be happy with us, as we did not expose any declaration file (the file telling TypeScript which vanilla JS thing has which type, see reference). In order to do that, we extend our tsconfig.json:
{
  "compilerOptions": {
    "declaration": true
  }
}Once we run pnpm build for our library once more (and publish it if necessary), VSCode understands the type of our imported button:

tsup
Compiling our 5 LOC, 1-component component library currently takes 2.4s on my machine. Once a library gets bigger, the compile time can grow significantly. Over the last years, a lot of fantastic Rust- and Go-based tooling has been developed. We're going to use tsup, which internally uses esbuild:
pnpm add -D tsupWe'll use the following tsup.config.ts config file:
import { defineConfig } from 'tsup'
 
export default defineConfig({
  entry: ['src/index.tsx'],
  dts: true,
  target: 'esnext',
  format: 'esm',
  sourcemap: true,
  minify: false
})Update the package.json accordingly:
{
  "scripts": {
    "build": "tsup"
  }
}Running pnpm build now takes 1.4s on my machine, and only 4ms of those are spent on actually compiling the library.
Watch mode
To avoid having to re-build the library everytime you change something, you can use the watch mode provided by tsup. Simply add a script to your package.json:
{
  "scripts": {
    "dev": "tsup --watch"
  }
}Running pnpm dev will now re-build the library on file changes.