Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigation: How to use in NextJs (or other server side rendering) #260

Closed
semoal opened this issue Aug 20, 2019 · 13 comments
Closed

Investigation: How to use in NextJs (or other server side rendering) #260

semoal opened this issue Aug 20, 2019 · 13 comments

Comments

@semoal
Copy link

semoal commented Aug 20, 2019

Hi!! Awesome library btw.

I didn't find any documentation about this use case, and i'll like to speak with some of you how this could be implemented.

I'm building a documentation previewer that is server side renderered (With react + nextjs) and i'll love to build a widget that let me pass from parent data to this iframe and re-render this view with the data that came from the parent.

I'll post here my advances on this. Thanks in advance.

@bluepnume
Copy link
Collaborator

This is totally possible with zoid. Question: would the re-render need to be server-side, or are you happy with that being on the client?

@semoal
Copy link
Author

semoal commented Aug 20, 2019

This is totally possible with zoid. Question: would the re-render need to be server-side, or are you happy with that being on the client?

The data comes from an express server , so I guess the re-render it's caused in server side.
My current working alternative is, entering in Puppetter and generating a screenshot of that page and returning a stream back, but it's pretty slow (3-5 seconds) and something that probably could be instant with a iframe.

I just have a server.js with:

server.post('/:page/b64', (req, res) => handleB64PDF(app, req, res, req.body.data));

async function handleB64PDF(app, req, res, external) {
  const timeStart = performance.now();
  const { query, params } = req;
  const { uuid, orientation } = query;
  const { page } = params;
  const pdfData = { external, orientation, uuid };

  let html = await app.renderToHTML(req, res, `/${page}`, pdfData);
  const { b64, timeToEnd } = await generatePdfFromHtml(html, isLandscape); // puppetter generator
  return b64;
}

@bluepnume
Copy link
Collaborator

OK. Well you can use

window.xprops.onProps(newProps => {
  // reload the page with new url, or something else
})

@semoal
Copy link
Author

semoal commented Aug 21, 2019

Got it working Daniel, I have some minor problems but ill fix them in a easy (or not easy way.. ;D)

I post a working example of SPA (App parent) and Nextjs SSR (App child).
https://gist.github.com/semoal/9ff2ac000040062d71ee6add04141dc1

Minor problems I've found:

  • I can't change url of the widget dynamically, example: adding a select with different views that loads through the same widget different urls. -> Now, doesn't reload. So I had to create different widgets for every template/page I have.
  • I've had to import Zoid.min.js file in a static folder because importing crash (due to window doesn't exist on server side rendering), so I've had to put ReactHelmet (or next's Head) and add a <script> attribute with src to the /static folder where Zoid.min.js is located:
    This crash on ssr:
import * as zoid from 'zoid/dist/zoid.frameworks';

This doesn't crash on ssr (due to timeline injection):

      render() {
        const { zoidProps } = this.state;
        return (
          <>
            <Head>
              <script src="/static/zoid.min.js"></script>
              <script src={`/static/widgets/${wantedWidget}-widget.js`}></script>
            </Head>
            <Page zoidProps={zoidProps} {...this.props} />
          </>
        )
}

Thanks for your help!

@semoal semoal closed this as completed Aug 30, 2019
@russelh15
Copy link

russelh15 commented Oct 22, 2019

@semoal Did you run into a race condition where window.xprops is undefined on initial render?

In the code below, the first console.log is undefined, but the second console.log shows the correct props.

import React from 'react'

export default () => {
  console.log('Window 1', window.xprops)

  React.useEffect(() => {
    setTimeout(() => {
      if (window.xprops) {
        console.log('Window 2', window.xprops)
      }
    }, 200)
  }, [])
  return <div>Testing</div>
}

I have a feeling it's something to do with NextHead not loading my widget setup until after the initial render, but if that's the case, you would experience that issue too.

@semoal
Copy link
Author

semoal commented Oct 23, 2019

@semoal Did you run into a race condition where window.xprops is undefined on initial render?

In the code below, the first console.log is undefined, but the second console.log shows the correct props.

import React from 'react'

export default () => {
  console.log('Window 1', window.xprops)

  React.useEffect(() => {
    setTimeout(() => {
      if (window.xprops) {
        console.log('Window 2', window.xprops)
      }
    }, 200)
  }, [])
  return <div>Testing</div>
}

I have a feeling it's something to do with NextHead not loading my widget setup until after the initial render, but if that's the case, you would experience that issue too.

No, i didn't mate.

Im currency doing other approach.

public componentDidMount(): void {
if (window.xprops) {
this.setState({ zoidProps: window.xprops });
console.log(window.xprops);
window.xprops.onProps((props: any) => this.setState({ zoidProps: props }));
}
}

passing that to state, i can directly use on render: this.state.zoidProps && this.state.zoidProps.value (for example)

Probably you're fist render is undefined due first the component is rendered on server where xprops is undefined, beucase window, and second one is workiung because window is realdy defined.

@russelh15
Copy link

@semoal Thanks for the reply. After much frustration, I got mine to work.

It turns out it was the order I was loading my scripts in _app.js. I did not realize the placement of really mattered, but apparently it does.

Side note, how are you handling urls in your widget.js file?

For example, i see you have it hardcoded to localhost, but how are you managing that in different environments like staging and production?

I have an approach that's working and I'm fairly happy with, but I'm curious to hear yours.

Thanks again for the reply!

@john093e
Copy link

john093e commented Dec 4, 2022

Hi @semoal @bluepnume @russelh15,

Could one of you please share a simple example of how to build an embbedable widget with Zoid and Next.js ?

I've try to put in place what @semoal shared in this conversation but couldn't succeed to make it work.

In advance thanks for your help !
Kind regards

@jasan-s
Copy link

jasan-s commented Apr 19, 2024

I'm trying to use zoid with child using Next js app and parent a standard HTML/css vanilla site.

Would love an example that shows this. Currently all I'm getting is a loading spinner on the parent.

@zukilover
Copy link

This is how I implement dynamic URL:

zoid.create({
  tag: 'widget-id',
  url: () => {
    try {
        const scriptSrc = document.getElementById('widget-id').getAttribute('src')
        return new URL(scriptSrc)?.origin
    } catch {}
  },
  dimensions: {
    width: "360px",
    height: "100vh",
  },
})

The widget is then used in the client page as follows:

<script src="%SERVER_URL%/widget.js" id="widget-id"></script>

@john093e
Copy link

john093e commented Aug 1, 2024

@zukilover could you please describe a bit more how you succeed to expose your zoid component from a NextJs app to third part ?

@john093e
Copy link

john093e commented Aug 1, 2024

@jasan-s did you succeed ?
if yes could you share your process ?

@john093e
Copy link

john093e commented Aug 3, 2024

Hello everyone! 😊

I’ve finally found a solution—maybe not the perfect one, but it works! I’ve put together a simple yet comprehensive example to share with you all: NextJs Zoid.

This is a Turborepo that includes both the Next.js app and the third-party website in a single project. While the main goal wasn’t to integrate Zoid within a Turborepo, I didn’t attempt to set it up as a separate package. However, I believe doing so could have some benefits.

Here are two examples:
Extremely simple (to reduce abstraction and help understand the minimum requirements): Button
A bit more complex: Advanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants