ryanseddon / react-frame-component

Render your React app to an iFrame
http://ryanseddon.github.io/react-frame-component/
MIT License
1.75k stars 156 forks source link

Add documentation on various techniques of handling CSS #242

Open ryanseddon opened 1 year ago

ryanseddon commented 1 year ago

This project gets a lot of questions on how to make CSS work inside the iframe from various CSS-in-JS libraries to CSS modules, SCSS etc.

Add some good examples covering how this can be done in the read me.

See latest issue with demo and code examples on solving that.

aboveyunhai commented 1 year ago

For Next.js and tailwindCSS, I cannot guarantee it will work consistently

  const { document: doc } = useFrame();

  useLayoutEffect(() => {
    // this covers development case as well as part of production
    document.head.querySelectorAll("style").forEach((style) => {
      const frameStyles = style.cloneNode(true);
      doc?.head.append(frameStyles);
    });
   // inject the production minified styles into the iframe
    if (process && process.env.NODE_ENV === "production") {
      document.head.querySelectorAll('link[as="style"]').forEach((ele) => {
        doc?.head.append(ele.cloneNode(true));
      });
      document.head
        .querySelectorAll('link[rel="stylesheet"]')
        .forEach((ele) => {
          doc?.head.append(ele.cloneNode(true));
        });
    }
  }, [doc]);
paulm17 commented 1 year ago

@aboveyunhai What version of next and what version of this lib are you running?

I'm running 13.1.1 and 5.2.6. All I get is a blank screen. If I regress to 4.1.3 of the lib. It works, but then I don't get the useFrame helper.

Found out here, that it's possibly a state issue: https://github.com/ryanseddon/react-frame-component/issues/192

I have partial success but then

const { document: doc } = useFrame();

Outputs the same as document. Both giving the same parent html and not the parent AND iframe.

Too many issues. If you could supply the full code, would be great! Thanks

aboveyunhai commented 1 year ago

@paulm17 it's a react issue, you need to call useFrame() inside the instead of at the same level.

const Inner = () => {
 useFrame();
}

const Outer = () => {
// useFrame();    call hook here will fail because it's outside of <Frame />

<Frame>
 <Inner/>
</Frame>
}
paulm17 commented 1 year ago

@aboveyunhai Thanks for the quick reply.

That's what I needed, I've written a wrapper function.

My code in case this helps someone else.

interface wrapperProps {
  children: React.ReactNode
}

function Wrapper({children}: wrapperProps) {
  const { document: doc } = useFrame();

  document.head.querySelectorAll("style").forEach((style) => {
    const frameStyles = style.cloneNode(true);
    doc?.head?.append(frameStyles);
  });   

  return <>{children}</>
}

This is how I'm using it in my proof of concept app.

const Home: NextPage = () => {  
  const [isMounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  return (
    <>
      <Head>
        <title>Create T3 App</title>
        <meta name="description" content="Generated by create-t3-app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      {isMounted && <Frame    
        id="frame"    
        style={{
          border: "none",
          width: "100%",
          height: "100vh",
        }}
      >
        <Wrapper>
          <Sortable/>       
        </Wrapper>
      </Frame>}
    </>
  );
};

I saw the examples from @ryanseddon but perhaps they work for CRA only? 🤷‍♂️

Anyway, I now have the latest 5.2.6 working with the help from the other thread.

aboveyunhai commented 1 year ago

@paulm17 you are missing my production insert part, so it will only work on dev, if you use build step npm run build, npm run start, the production probably will miss all the stylings.

paulm17 commented 1 year ago

proof of concept app.

Thanks, but you missed the part where I said ^. The app won't ever see production.

sscaff1 commented 1 month ago

If anyone is using react-jss. This can help:

  1. Create a wrapper for JSSProvider
    
    import { create } from 'jss';
    import { ReactNode } from 'react';
    import { useFrame } from 'react-frame-component';
    import { JssProvider } from 'react-jss';
    import preset from 'jss-preset-default';

const JssProviderWrapper = ({ children }: { children: ReactNode }) => { const { document } = useFrame(); const jss = create({ insertionPoint: document.head }); jss.setup(preset()); return {children}; };

export default JssProviderWrapper;


Then use it:

```tsx
const App = () => (
  <Frame initialContent={initialContent}>
    <JssProviderWrapper>
      <MyApp />
    </JssProviderWrapper>
  </Frame>
);
sakib412 commented 1 month ago

How to load css from installed packages(node_modules)? For example: import '@ezy/ui/styles.css'; How to load this css inside iframe, currently in link tag only public route css is working.