Skip to content

Commit

Permalink
feat: migrate to react@19
Browse files Browse the repository at this point in the history
- SSR-only now. entire html is rendered in JSX and hydrated into
- using esm.sh for client react
  • Loading branch information
AviVahl committed Dec 6, 2024
1 parent ab732b7 commit 8d1ede4
Show file tree
Hide file tree
Showing 15 changed files with 103 additions and 240 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Native esm React example.

- An example [React](https://github.com/facebook/react) application rendering two svg logos.
- An http server that:
- serves the above application, providing both the SSR (`http://localhost:3000/`) and client-only (`http://localhost:3000/index.html`) approaches.
- serves the above application using SSR at `http://localhost:3000/`
- uses a worker thread to separate app evaluation/rendering from http server.
- supports live reloading of the SSR renderer and connected clients.
- is not production-ready, as there's no caching, compression, etc.
Expand All @@ -15,7 +15,7 @@ Native esm React example.
- Asset references using `new URL('./asset.svg, import.meta.url)` are shown and work for SSR as well. Assets must live outside the `src` tree so relative references from `dist` work.
- A _really_ cool `"start"` script that triggers server reloading while giving a `tsc -w` like experience.
- Tiny amount of dev/runtime dependencies. `npm i` and look at `node_modules`.
- React itself is _not_ published as native ESM, so the files in the `static/esm-bridge` folder allow browser ESM imports to use the UMD versions of `react`/`react-dom`, which are loaded by `index.html`.
- React itself is _not_ published as native ESM, so the application uses https://esm.sh to load client-side React.
- Written using strict TypeScript.

## Getting Started
Expand Down
38 changes: 0 additions & 38 deletions index.html

This file was deleted.

77 changes: 22 additions & 55 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
},
"dependencies": {
"mime": "^4.0.4",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "22",
"@types/react": "^18.3.13",
"@types/react-dom": "^18.3.1",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/ws": "^8.5.13",
"playwright": "^1.49.0",
"typescript": "~5.7.2",
Expand Down
2 changes: 1 addition & 1 deletion scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const statusReporter = (diagnostic, newLine, options, errorCount) => {

const watchCompilerHost = createWatchCompilerHost(
tsconfigPath,
{ noUnusedLocals: false, noUnusedParameters: false },
{ noUnusedLocals: false, noUnusedParameters: false, jsx: ts.JsxEmit.ReactJSXDev },
sys,
undefined,
undefined,
Expand Down
68 changes: 47 additions & 21 deletions src/client/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,53 @@ import React from "react";
const reactLogo = new URL("../../static/react-logo.svg", import.meta.url);
const html5Logo = new URL("../../static/html5-logo.svg", import.meta.url);

export interface AppProps {
renderType: string;
}

export const App: React.FC<AppProps> = ({ renderType }) => {
export const App: React.FC = () => {
return (
<>
<header style={{ paddingTop: "2em" }}>
<img width={120} height={120} src={reactLogo.href} alt="react"></img>
<img width={120} height={120} src={html5Logo.href} alt="html5"></img>
</header>
<main>
<h1>Native ESM React Example</h1>
<section>
Showcase an approach to load a native ESM React application in the
browser and Node.js.
</section>
<section style={{ marginTop: "1em" }}>
Current rendering: {renderType}
</section>
</main>
</>
<html lang="en">
<head>
<meta charSet="UTF-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="native esm react example" />
<title>My App</title>
<style>{defaultStyle}</style>
<script type="importmap">{JSON.stringify(importMap)}</script>
</head>
<body>
<header style={{ paddingTop: "2em" }}>
<img width={120} height={120} src={reactLogo.href} alt="react"></img>
<img width={120} height={120} src={html5Logo.href} alt="html5"></img>
</header>
<main>
<h1>Native ESM React Example</h1>
<section>
Showcase an approach to load a native ESM React application in the
browser and Node.js.
</section>
</main>
<script type="module" src="dist/client/main.js"></script>
</body>
</html>
);
};

const importMap = {
imports: {
react: `https://esm.sh/react@${React.version}`,
"react/jsx-runtime": `https://esm.sh/react@${React.version}/jsx-runtime`,
"react/jsx-dev-runtime": `https://esm.sh/react@${React.version}/jsx-dev-runtime`,
"react-dom": `https://esm.sh/react-dom@${React.version}`,
"react-dom/client": `https://esm.sh/react-dom@${React.version}/client`,
},
};

const defaultStyle = `
:root {
color-scheme: dark;
font-family: sans-serif;
}
body {
text-align: center;
}
`;
27 changes: 5 additions & 22 deletions src/client/main.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
import React, { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { App } from "./app.js";

const rootContainerId = "SITE_MAIN";

const container =
document.getElementById(rootContainerId) ?? createContainer(document.body);

const isSSR = container.hasAttribute("data-ssr");
const appElement = (
hydrateRoot(
document,
<StrictMode>
<App renderType={isSSR ? "server-side" : "client-side"} />
<App />
</StrictMode>
);

if (isSSR) {
ReactDOM.hydrateRoot(container, appElement);
} else {
ReactDOM.createRoot(container).render(appElement);
}

function createContainer(targetParent: Element) {
const newContainer = document.createElement("div");
newContainer.id = rootContainerId;
return targetParent.appendChild(newContainer);
}
Loading

0 comments on commit 8d1ede4

Please sign in to comment.