···11+# Welcome to Remix + Cloudflare Workers!
22+33+- 📖 [Remix docs](https://remix.run/docs)
44+- 📖 [Remix Cloudflare docs](https://remix.run/guides/vite#cloudflare)
55+66+## Development
77+88+Run the dev server:
99+1010+```sh
1111+npm run dev
1212+```
1313+1414+To run Wrangler:
1515+1616+```sh
1717+npm run build
1818+npm start
1919+```
2020+2121+## Typegen
2222+2323+Generate types for your Cloudflare bindings in `wrangler.toml`:
2424+2525+```sh
2626+npm run typegen
2727+```
2828+2929+You will need to rerun typegen whenever you make changes to `wrangler.toml`.
3030+3131+## Deployment
3232+3333+If you don't already have an account, then [create a cloudflare account here](https://dash.cloudflare.com/sign-up) and after verifying your email address with Cloudflare, go to your dashboard and set up your free custom Cloudflare Workers subdomain.
3434+3535+Once that's done, you should be able to deploy your app:
3636+3737+```sh
3838+npm run deploy
3939+```
4040+4141+## Styling
4242+4343+This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever css framework you prefer. See the [Vite docs on css](https://vitejs.dev/guide/features.html#css) for more information.
+18
app/entry.client.tsx
···11+/**
22+ * By default, Remix will handle hydrating your app on the client for you.
33+ * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
44+ * For more information, see https://remix.run/file-conventions/entry.client
55+ */
66+77+import { RemixBrowser } from "@remix-run/react";
88+import { startTransition, StrictMode } from "react";
99+import { hydrateRoot } from "react-dom/client";
1010+1111+startTransition(() => {
1212+ hydrateRoot(
1313+ document,
1414+ <StrictMode>
1515+ <RemixBrowser />
1616+ </StrictMode>
1717+ );
1818+});
+56
app/entry.server.tsx
···11+/**
22+ * By default, Remix will handle generating the HTTP Response for you.
33+ * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
44+ * For more information, see https://remix.run/file-conventions/entry.server
55+ */
66+77+import type { AppLoadContext, EntryContext } from "@remix-run/cloudflare";
88+import { RemixServer } from "@remix-run/react";
99+import { isbot } from "isbot";
1010+import { renderToReadableStream } from "react-dom/server";
1111+1212+const ABORT_DELAY = 5000;
1313+1414+export default async function handleRequest(
1515+ request: Request,
1616+ responseStatusCode: number,
1717+ responseHeaders: Headers,
1818+ remixContext: EntryContext,
1919+ // This is ignored so we can keep it in the template for visibility. Feel
2020+ // free to delete this parameter in your app if you're not using it!
2121+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2222+ loadContext: AppLoadContext
2323+) {
2424+ const controller = new AbortController();
2525+ const timeoutId = setTimeout(() => controller.abort(), ABORT_DELAY);
2626+2727+ const body = await renderToReadableStream(
2828+ <RemixServer
2929+ context={remixContext}
3030+ url={request.url}
3131+ abortDelay={ABORT_DELAY}
3232+ />,
3333+ {
3434+ signal: controller.signal,
3535+ onError(error: unknown) {
3636+ if (!controller.signal.aborted) {
3737+ // Log streaming rendering errors from inside the shell
3838+ console.error(error);
3939+ }
4040+ responseStatusCode = 500;
4141+ },
4242+ }
4343+ );
4444+4545+ body.allReady.then(() => clearTimeout(timeoutId));
4646+4747+ if (isbot(request.headers.get("user-agent") || "")) {
4848+ await body.allReady;
4949+ }
5050+5151+ responseHeaders.set("Content-Type", "text/html");
5252+ return new Response(body, {
5353+ headers: responseHeaders,
5454+ status: responseStatusCode,
5555+ });
5656+}