- v2.3.8Latest
- v2.3.7
- v2.3.6
- v2.3.5
- v2.3.4
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.7
- v2.1.7
- v2.1.6
- v2.1.5
- v2.1.4
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.1
- v2.0.0
- v2.0.0-beta.19
- v2.0.0-beta.18
- v2.0.0-beta.17
- v2.0.0-beta.16
- v2.0.0-beta.15
- v2.0.0-beta.14
- v2.0.0-beta.13
- v2.0.0-beta.12
- v2.0.0-beta.11
- v2.0.0-beta.10
- v2.0.0-beta.9
- v2.0.0-beta.8
- v2.0.0-beta.7
- v2.0.0-beta.6
- v2.0.0-beta.5
- v2.0.0-beta.4
- v2.0.0-beta.3
- v2.0.0-beta.2
- v2.0.0-beta.1
- v2.0.0-alpha.19
- v2.0.0-alpha.18
- v2.0.0-alpha.17
- v2.0.0-alpha.16
- v2.0.0-alpha.15
- v2.0.0-alpha.14
- v2.0.0-alpha.13
- v2.0.0-alpha.12
- v2.0.0-alpha.11
- v2.0.0-alpha.10
- v2.0.0-alpha.9
- v2.0.0-alpha.8
- v2.0.0-alpha.7
- v2.0.0-alpha.6
- v2.0.0-alpha.5
- v2.0.0-alpha.5
- v2.0.0-alpha.4
- v2.0.0-alpha.3
- v2.0.0-alpha.2
- v2.0.0-alpha.1
- v2.0.0-alpha.0
- v1.0.1
- v1.0.0
- v0.8.2
- v0.8.1
- v0.8.0
- v0.7.6
- v0.7.5
- v0.7.4
- v0.7.3
- v0.7.2
- v0.7.1
- v0.7.0
- v0.7.0
- v0.6
- v0.5
- v0.4
- v0.3
- v0.2
- v0.1
- v0.0
Deno, ESM + React: No build, no bundle, all streaming
Ultra is a web framework that leans hard into your browserâs native features. Embrace the future of ES Modules, Import Maps, and Web Streams. All while supporting some of the non-standards that many normal people love for some reason (JSX and TypeScript).
Itâs driven by the following hot-takes:
- ESM is non-negotiable in {currentYear}
- SSR is non-negotiable in {currentYear}
- Bundling is an anti-pattern in {currentYear}
- Data can be requested anywhere, and is accessible on the server, always
- Lazy routing with dynamic imports trumps FS routing
- Less magic in tooling and frameworks is a good thing
- Simplify your workflow and tech stack at all costs - life is too short
- Streams are neat
Warning: The following is built around the alpha version of React 18. Mileage may vary.
Demo: Here is a port of the React 18 SSR demo which showcases Suspense SSR.
Quick start
The most minimal setup of Ultra can be found at /examples/boilerplate. There are more /examples as well.
Native first
Everything is ES Modules. Server side rendering is default. Have the quickest TTFB by using the React streaming server renderer.
# HTTP/2 200
* Received 381 B chunk
* Received 8 B chunk
* Received 6 B chunk
* Received 6 B chunk
* Received 1 B chunk
* Received 5 B chunk
* Received 2 B chunk
* Received 7 B chunk
Import Maps are used to manage 3rd party dependencies. No bundling, building or complex package managers needed.
{
"imports": {
"react": "https://esm.sh/react@alpha",
"react-dom": "https://esm.sh/react-dom@alpha"
}
}
import React from "react";
export default const Graveyard = () => {
const gravestones = [
".cjs",
"require()",
"node_modules",
"package.json",
"webpack.config",
"babel.config",
"create-react-app",
"next.js",
];
return (
<ul className="graveyard">
{gravestones.map((grave) => (
<li>
<figure>
<img src="/grave.svg" alt="Gravestone" />
<figcaption>{grave}</figcaption>
</figure>
</li>
));}
</ul>
)
};
Under the hood: We use esbuild + SWC to transpile jsx/tsx in realtime. Your single ES modules stay single ES modules, but as minified vanilla js, with your import maps inlined.
Transpile: graveyard.jsx in 6ms
// Transpiled example of graveyard.jsx
import e from"https://esm.sh/react@alpha";const a=()=>e.createElement("ul",{className:"graveyard"},[".cjs","require()","node_modules","package.json","webpack.config","babel.config","create-react-app","next.js"].map(r=>e.createElement("li",null,e.createElement("figure",null,e.createElement("img",{src:"/grave.svg",alt:"Gravestone"}),e.createElement("figcaption",null,r)))));export default a;
Note: In development, modules are transpiled every request. In production, transpiled modules are stored in an LRU cache. đ
Routing
Stop poking around at your filesystem. Routing can be defined anywhere in your app, and dynamic imports will ensure only relevant route files are downloaded at any given time.
Powered by Wouter. Ah, what a breath of fresh airâŚ
import { Suspense } from "react";
import { Router } from "wouter";
const Home = lazy(() => import("./home.jsx"));
const App = () => {
<Router>
<Suspense path="/home" fallback={<Loading />}>
<Home />
</Suspense>
</Router>;
};
Data fetching
SWR lets us fetch data anywhere in our components, works with Suspense everywhere.
Caveat: Suspense data fetching is still in itâs early days⌠watch this space.
import { Suspense } from "react";
import useSWR from "swr";
const Profile = () => {
const { data } = useSWR("/api/user", fetcher, { suspense: true });
return <div>hello, {data.name}</div>;
};
const App = () => {
return (
<Suspense fallback={<Loading />}>
<Profile />
</Suspense>
);
};