Open seanmcquaid opened 1 year ago
Update on this, might have a working example that follows some previous code ya'll added! Going to try and contribute an example for this when I get some time
this one works for me import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite';
// https://vitejs.dev/config/ export default defineConfig({ plugins: [ react({ include: /.(jsx|tsx)$/, babel: { plugins: ['styled-components'], babelrc: false, configFile: false, }, }), ], });
@seanmcquaid hey, have you figured out how to make Remix + Vite work with styled-components
?
I did! Here are the links to the entry.server.tsx + root.tsx files, it was really just following what was already in place.
https://github.com/seanmcquaid/remix-unstable-vite-playground/blob/main/app/entry.server.tsx#L38-L55
https://github.com/seanmcquaid/remix-unstable-vite-playground/blob/main/app/root.tsx#L37
This solution worked for me: https://github.com/styled-components/styled-components/issues/4275
I was having such as the style mismatches and "Identifier 'RefreshRuntime' has already been declared"
I was able to get this working fully with no errors by doing this.
Base babel plugin ensures there's no clash in react refresh with remix.
/\.(t|j)sx?$/,
for the filter is also key, ensuring it matches typescript.
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import babel from 'vite-plugin-babel';
export default defineConfig({
plugins: [
babel({
filter: /\.(t|j)sx?$/,
babelConfig: {
babelrc: false,
configFile: false,
presets: [
'@babel/preset-typescript'
],
plugins: [
[
'babel-plugin-styled-components',
{
displayName: true,
ssr: true,
fileName: false
}
]
],
}
}),
remix(),
tsconfigPaths(),
],
});
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
import { createHead } from "remix-island";
export const Head = createHead(() => (
<>
<Meta />
<Links />
</>
));
export default function App() {
return (
<>
<Head />
<Outlet />
<ScrollRestoration />
<Scripts />
</>
)
}
import { PassThrough } from "node:stream";
import type { AppLoadContext, EntryContext } from "@remix-run/node";
import { createReadableStreamFromReadable } from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";
import { ServerStyleSheet } from "styled-components";
import { Head } from './root';
import { renderHeadToString } from "remix-island";
const ABORT_DELAY = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
// This is ignored so we can keep it in the template for visibility. Feel
// free to delete this parameter in your app if you're not using it!
// eslint-disable-next-line @typescript-eslint/no-unused-vars
loadContext: AppLoadContext
) {
return isbot(request.headers.get("user-agent") || "")
? handleBotRequest(
request,
responseStatusCode,
responseHeaders,
remixContext
)
: handleBrowserRequest(
request,
responseStatusCode,
responseHeaders,
remixContext
);
}
function handleBotRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
const sheet = new ServerStyleSheet();
const jsx = sheet.collectStyles(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
);
const { pipe, abort } = renderToPipeableStream(
jsx,
{
onAllReady() {
shellRendered = true;
const styles = sheet.getStyleTags();
const head = renderHeadToString({ request, remixContext, Head });
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);
body.write(
`<!DOCTYPE html><html lang="en"><head><!--start head-->${head}<!--end head-->${styles}</head><body><div id="root">`
);
pipe(body);
body.end("</div></body></html>");
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
// Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest.
if (shellRendered) {
console.error(error);
}
},
}
);
setTimeout(abort, ABORT_DELAY);
});
}
function handleBrowserRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
const sheet = new ServerStyleSheet();
const jsx = sheet.collectStyles(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
);
const { pipe, abort } = renderToPipeableStream(
jsx,
{
onShellReady() {
shellRendered = true;
const styles = sheet.getStyleTags();
const head = renderHeadToString({ request, remixContext, Head });
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);
body.write(
`<!DOCTYPE html><html lang="en"><head><!--start head-->${head}<!--end head-->${styles}</head><body><div id="root">`
);
pipe(body);
// This is key instead of body.write for suspense and lazy loading
body.end("</div></body></html>");
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
// Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest.
if (shellRendered) {
console.error(error);
}
},
}
);
setTimeout(abort, ABORT_DELAY);
});
}
/**
* By default, Remix will handle hydrating your app on the client for you.
* 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` ✨
* For more information, see https://remix.run/file-conventions/entry.client
*/
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
startTransition(() => {
hydrateRoot(
document.getElementById("root")!,
<StrictMode>
<RemixBrowser />
</StrictMode>
);
});
More of a request but is there any chance we can get an ideal example for using Styled Components with Vite? I tried my hand at it but would love to see something here to go off of.