The App.tsx
file has been a little bit goofy. This release cleans that up. Instead of needing to import the app file into both entry-server
and entry-client
, we've now got the concept of a "root route". This root route works like all others, it can export a loader and error boundary, etc.
This small change removes a few APIs and a lot of complexity in Remix's internals. It was one of those -200 line changes without losing any features, which always feels amazing.
Move App.tsx
to root.tsx
Move your App.tsx
to root.tsx
. "root" has special meaning, so it must be named that.
Change <Routes/>
to <Outlet/>
Now that root.tsx
is a route, you render an Outlet just like every other layout route.
// OLD
import { Routes } from "remix";
export default function App() {
return (
<html>
{/* ... */}
<body>
<Routes />
</body>
</html>
);
}
// NEW
// Note it's from "react-router-dom"!
import { Outlet } from "react-router-dom";
export default function App() {
return (
<html>
{/* ... */}
<body>
<Outlet />
</body>
</html>
);
}
Move global data code
Because root.tsx
iss now a route it can export a loader for it's own data. If you had a global-data.ts
file, cut and paste the code into root.tsx
so it's co-located like all of your other routes.
Also, there is no longer a useGlobalData
hook because this is now normal route data. So change your useGlobalData()
to useRouteData()
.
Update your server and browser entries
Don't import or render App.tsx
in entry-server
or entry-browser
anymore:
// OLD: entry-server
import App, { ErrorBoundary } from "./App";
let markup = ReactDOMServer.renderToString(
<Remix
context={remixContext}
url={request.url}
ErrorBoundary={ErrorBoundary}
>
<App />
</Remix>
);
// NEW: entry-server
// - no more App import because you moved it to `root.tsx`
// - no more ErrorBoundary prop because the root route exports
// it's own error boundary like any other route
let markup = ReactDOMServer.renderToString(
<Remix context={remixContext} url={request.url} />
);
Do the same thing in entry-browser.tsx
, it'll end up looking something like this:
import ReactDOM from "react-dom";
import Remix from "@remix-run/react/browser";
ReactDOM.hydrate(<Remix />, document);
Much nicer! No more special casing the root layout. We're also thinking this opens up the possibility for multiple root layouts: think signed-in-layout and signed-out-layout". We're not there yet though :)
You can now import and resize images as a JavaScript module containing links to the assets, height and width attributes, and even responsive image source sets. Check it out!
// - change quality to 50
// - reformat to avif
// - generate a server rendered Base64 blurry placeholder
// - resize to 500
import guitar from "img:./guitar.jpg?quality=50&format=avif&placeholder&width=500";
// - change quality to 80
// - generate 3 responsive image sizes and a srcset for the `<img srcSet/>`
// - generate a ssr placeholder
import guitar2 from "img:./guitar.jpg?quality=80&srcset=720,1080,2048&placeholder";
export default function Guitar() {
return (
<div>
<p>Fixed Image</p>
<img
alt="Guitar"
src={guitar.src}
style={{
backgroundImage: `url(${guitar.placeholder})`,
backgroundSize: "cover"
}}
width={guitar.width / 2}
height={guitar.height / 2}
/>
<p>Responsive</p>
<img
alt="Guitar"
src={guitar2.src}
srcSet={guitar2.srcset}
style={{
backgroundImage: `url(${guitar2.placeholder})`,
backgroundSize: "cover"
}}
/>
</div>
);
}