styled-components / styled-components

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅
https://styled-components.com
MIT License
40.11k stars 2.48k forks source link

RNW styles applied twice when using nextjs app directory with react-native-web with SSR #4270

Open macrozone opened 2 months ago

macrozone commented 2 months ago

Environment

System:

Reproduction

here is a link to a repository that reproduces the problem:

https://github.com/macrozone/styled-components-rnw-reproduction

I added multiple scenarios.

Best run it locally (check it out, then yarn dev or npm run dev) and go to localhost:3000

it shows links to multiple scenarios. The first one shows the actual problem reported here.

The others are cases work correctly (but are probably not what you want or have).

Steps to reproduce (as in the repo above)

I wrote these before creating the reproduction, so it may be easier to checkout the repo.

import { StyleProvider } from "./StyleProvider";

export const metadata = {
  title: "Next.js",
  description: "Generated by Next.js",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <StyleProvider>{children}</StyleProvider>
      </body>
    </html>
  );
}

and StyleProvider like this:

"use client";

import { useServerInsertedHTML } from "next/navigation";
import React, { useState } from "react";
import { StyleSheet } from "react-native";
import { ServerStyleSheet, StyleSheetManager } from "styled-components";

export const StyleProvider = ({ children }: { children: React.ReactNode }) => {
  const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());

  useServerInsertedHTML(() => {
    // @ts-ignore
    const rnwStyle = StyleSheet.getSheet();
    const styledComponentsStyle = styledComponentsStyleSheet.getStyleElement();
    styledComponentsStyleSheet.instance.clearTag();

    return (
      <>
        <style
          dangerouslySetInnerHTML={{ __html: rnwStyle.textContent }}
          id={rnwStyle.id}
        />
        {styledComponentsStyle}
      </>
    );
  });

  if (typeof window !== "undefined") return <>{children}</>;

  return (
    <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
      {children}
    </StyleSheetManager>
  );
};
"use client";
import styled from "styled-components/native";
import { View, Text } from "react-native";
import { useEffect, useState } from "react";

const Base = styled(View)`
  padding: 20px;
  margin: 20px;
  background-color: #f0f0f0;
  border: 1px solid red;
`;

const TheText = styled(Text)`
  color: green;
`;

export const Test = () => {
  const [show, setShow] = useState(false);

  useEffect(() => {
    setShow(true);
  }, []);

  return show ? (
    <Base>
      <TheText>Hello</TheText>
    </Base>
  ) : (
    <TheText>no show</TheText>
  );
};

and finally page.tsx:

import { Test } from "./Test";

export default Test;

Expected Behavior

Bildschirmfoto 2024-02-29 um 17 00 49

Actual Behavior

Bildschirmfoto 2024-02-29 um 17 01 04

Context & more information

  1. When looking at the SSR rendered result, you see that it shows no show in green, so SSR contains indeed styling information
  2. to see the "Actual Behavior", remove the <StyleProvider /> in the layout. This results in the SSR result beeing unstyled though, which is not what we want
  3. The Problem does not happen if you use react-native's styleSheets. There both SSR result and local rendering are consistent, so it looks like it has to be a styled-components problem, not a ReactNativeWeb problem
  4. the problem happens if Styled-components mount on the client, but not on the server
  5. extracting the Styled-components styles in the StyleProvider does nothing, but i kept it in the example for further reference. It was not clear to me whether to add it or not.
  6. the problem is similar to what is described here https://github.com/necolas/react-native-web/issues/1367
macrozone commented 2 months ago

update: added a link to a reproduction repo https://github.com/macrozone/styled-components-rnw-reproduction