bluwy / whyframe

Develop components in isolation with just an iframe
https://whyframe.dev
MIT License
549 stars 11 forks source link

Add ability to server render the frame #24

Open ceopaludetto opened 1 year ago

ceopaludetto commented 1 year ago

I'm creating a docs website using whyframe and astro, the react integration works pretty well, but every reload on the page causes a little flash in the iframe, so after reading the source code i've imagined if we can expose a createServerApp from the virtual import:

---
// /frames/default.astro
import { createServerApp } from "whyframe:app"

const html = await createServerApp(Astro.url.searchParams.get("frame-id"))
---

<Fragment set:html={html} />

So instead of collecting the id from dataset attributes we can send directly in the frame URL, like so:

<iframe src="/frames/default?frame-id=some-id" />

And then, in the createServerApp internals we dynamic import the frame data and render with the matched framework function(e.g. renderToString). What do you think?

bluwy commented 1 year ago

Hi. Yeah currently createApp() only implements client-side rendering. createServerApp is an interesting idea, my initial concern would be how to generate both the SSR code and hydration code during build. Otherwise it would be uninteractable.

Regarding the flash, I'm not entirely sure if SSR would solve this too. IIRC browsers only fetch the iframe resources after the page is loaded, so a flash would always be visible unless it's already cached. CSR is usually fairly fast too and for iframes they can't be indexed by a crawler.

bluwy commented 1 year ago

I actually also have a script to make the flash not so obvious/jarring: https://github.com/bluwy/whyframe/blob/72e34498748f0b45d9272c886e123efd4dd24e17/docs/src/components/IframeNoFlash.astro

ceopaludetto commented 1 year ago

Hi again, I initially write this issue because I've imagined that SSR could speed up the show source process, but after investigation I realized that the source code is always available(not only in the client side). So instead of getting the source code using the util getWhyframeSource I was able to get by accessing the props(this could be included in the docs somehow?):

type AddCodeProps = {
  children: ReactElement;
};

function AddCode({ children }: AddCodeProps) {
  const code = children.props["data-why-source"]; // here is the magic

  return (
    <>
      {children}
      <Suspense>
        <Code className="rounded-t-none border-t-0" lang="tsx">
          {code}
        </Code>
      </Suspense>
    </>
  );
}

export type StoryProps = {
  children: ReactNode;
};

export function Story({ children }: StoryProps) {
  return (
    <AddCode>
      <iframe data-why className="w-full rounded-t-md border border-surface-container-high">
        {children}
      </iframe>
    </AddCode>
  );
}

This allow the syntax highlight to render on the server side even if the iframe is not loaded. For the flash I'll use the script which seems to be a better solution, thank you!

bluwy commented 1 year ago

Yup, you can definitely use children.props["data-why-source"] as a fast path for now. I didn't document this because it's meant to be internal. But given how long I haven't changed that I guess it's fair to document that.

I still have plans to change that up in the future, but it'll be part in a new set of breaking changes anyway (that I still haven't got to finish up).