Недавно мне было поручено изучить жизнеспособность использования Remix в качестве инструмента для EvidencCare, и одна из вещей, которая стала совершенно очевидной, — это отсутствие способа включения Styled Components из-за ряда факторов.

Некоторые решения требовали от вас изменения функции handleDataRequest или файла entry.client.js, но их не было. Даже в Документации по ремиксам упоминаются открытые проблемы Github, что приводит к еще большему количеству открытых проблем для React SSR. Другие предложенные решения включали рендеринг приложения в div и даже зашли так далеко, что создали пакеты Node, чтобы облегчить это, но лидеры сообщества React в дальнейшем отвергли их как хорошие. Некоторые даже поддерживают изменение процесса сборки. Даже документы Styled Component были слишком универсальными.

Итак, вот что у меня работало на момент написания этой статьи:

Вот server.js, как он начинается, когда вы инициализируете новый проект:

import { createRequestHandler } from "@remix-run/architect";
import * as build from "@remix-run/dev/server-build";
export const handler = createRequestHandler({
  build,
  mode: process.env.NODE_ENV,
});

Я использовал Typescript, чтобы выяснить, как вручную добавить это значение build, чтобы я мог получить доступ к этой функции handleDocumentRequest, на которую ссылаются некоторые учебные пособия, и фактически реализовать часть решения:

import { createRequestHandler } from "@remix-run/architect";
import * as build from "@remix-run/dev/server-build";
import { ServerStyleSheet } from "styled-components";
import { renderToString } from "react-dom/server";
import { RemixServer } from "@remix-run/react";

function handleDocumentRequest(
  request,
  responseStatusCode,
  responseHeaders,
  remixContext
) {
  const sheet = new ServerStyleSheet();

  let markup = renderToString(
    sheet.collectStyles(
      <RemixServer
        context={remixContext}
        url={request.url}
      />
    )
  );
  const styles = sheet.getStyleTags();
  markup = markup.replace("__STYLES__", styles);

  responseHeaders.set("Content-Type", "text/html");

  return new Response("<!DOCTYPE html>" + markup, {
    status: responseStatusCode,
    headers: responseHeaders,
  });
}

export const handler = createRequestHandler({
  build: {
    routes: build.routes,
    entry: {
      module: {
        default: handleDocumentRequest,
        handleDataRequest: build.entry.module.handleDataRequest
      }
    },
    assets: build.assets,
    dev: {liveReloadPort: build.dev?.liveReloadPort},
    future: build.future,
    publicPath: build.publicPath,
    assetsBuildDirectory: build.assetsBuildDirectory
  },
  mode: process.env.NODE_ENV,
});

Мой файл root.tsx также добавил эту строку-заполнитель:

import type {LinksFunction, MetaFunction} from "@remix-run/node";
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";
import stylesheet from "~/tailwind.css";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: stylesheet },
];

export const meta: MetaFunction = () => ({
  charset: "utf-8",
  title: "New Remix App",
  viewport: "width=device-width,initial-scale=1",
});

export default function App() {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
        {typeof document === "undefined"
          ? "__STYLES__"
          : null}
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        {/*// https://github.com/remix-run/remix/issues/198*/}
        {/*<LiveReload />*/}
      </body>
    </html>
  );
}

Это «технически» работает. Однако известная проблема гидратации приводит к тому, что режим SPA включается во время разработки, что не позволяет ему работать для локальной разработки. Чтобы исправить это, я просто живу без перезагрузки в реальном времени, и почему вы видите, что это закомментировано. Браузер просто перезагрузится при изменении кода. Это не так идеально, но со временем это будет исправлено.

С помощью этих шагов Styled Components работает в Remix.