React-snap: Way to prerender pages with some extra settings

Created on 9 Feb 2018  路  22Comments  路  Source: stereobooster/react-snap

I need prerendered pages for three cases: unlogged user, unlogged user from mobile and logined user. Currently i can do this only by insert some hacks into app code, which is not very good at my opinion.
So, can react-snap handle this cases in some way? Ability to set viewport or cookie for url will solve my problem.

question

Most helpful comment

@Amareis I am building my application through package.json rather than calling postbuild : "react-snap".
I Changed it to postbuild: "node reactSnapConfig.js"

And for serving the snapshot I am handling it at nginx based on userAgent.

//reactSnapConfig.js
const { run } = require("react-snap");

run({
source: "build",
destination: "mobile",
"viewport": {
"width": 480,
"height": 850
}
});

run({
source: "build",
destination: "desktop",
"viewport": {
"width": 1600,
"height": 1200
}
});

This did the trick for me as i have huge build system and multiple modules.

Thanks again @stereobooster for the awesome job.

All 22 comments

You can set viewport (related https://github.com/stereobooster/react-snap/issues/138):

"reactSnap": {
  "viewport": {
    "width": 480,
    "height": 850
  }
}

If you want to add logic over how pages are rendered you can use programmatic API (there is only one root function). Related https://github.com/stereobooster/react-snap/issues/126

Yeah, I know about this argument. But I need both mobile and not mobile pages, is it can be achieved only via programmatic api?
If yes, and if I call run function multiple times with different args, will react-snap share chromium instance between this calls, or will restarts it on each call?
And how I can set some cookies for run?

Let's assume there is only one root page (/) on the website. It will be rendered as /index.html (it is mobile viewport by default). How do you expect desktop version of this page to be stored?

We use cloudfront with custom routing lambda, which rewrite request on correct html file. So I just need three html files (index.html, mobile.html and chat.html).

We use cloudfront with custom routing lambda, which rewrite request on correct html file. So I just need three html files (index.html, mobile.html and chat.html).

This sentence doesn't answer my question

How do you expect desktop version of this page to be stored?

Just somehow. Filename can be just another extra setting.

With that settings you even can handle query and hash args, which are dropped here now.

Idea behind the project is simplicity. You give it react application it gives you back prerendered website which you can upload to general static website hosting, like S3.

For your case you can use programmatic API:

const { run } = require("react-snap");

run({
  source: "build",
  destination: "mobile",
  "viewport": {
    "width": 480,
    "height": 850
  }
});

run({
  source: "build",
  destination: "desktop",
  "viewport": {
    "width": 1600,
    "height": 1200
  }
});

What about cookies? And will library reuses chrome instance?

React App can be much more complicated, especially when Router and authentication come into play.

Each run will have one separate instance of chrome.

What about cookies?

What about it? There is a real browser behind it. As far as I know data not stored between runs.

React App can be much more complicated, especially when Router and authentication come into play.

Not sure what do you mean, but if there is a lot of dynamic content, react-snap will not fit. Use SSR in that case.

What about it

Can I set cookies for session?

Not sure what do you mean, but if there is a lot of dynamic content, react-snap will not fit. Use SSR in that case.
Idea behind the project is simplicity. You give it react application it gives you back prerendered website which you can upload to general static website hosting, like S3.

Of course I know 'bout SSR. But even without any dynamic content we can have more than one state behind one URL, as in my case, so library should be able to handle it someway to be true pre-render library.

Can I set cookies for session?

If you mean something like this code in your application:

Cookies.set('theme', 'green', {expiry : 3600});

Yes, you can. Not sure, though, how it will work. I do not understand what you are trying to achieve.

No, I was talking about cookies via react-snap config, just as viewport property. But I already see what it cannot be done in current version.

Okay, I made these changes in my fork https://github.com/Amareis/react-snap but it's not for PR at this moment (at least, filename option make unnessesary *.html routes additional logic). You can see changes and if you want, I made a PR.
There is working config sample (include arrays also supported):

  "reactSnap": {
    "source": "dist",
    "include": {
      "/": {
        "viewport": {
          "width": 1366,
          "height": 768
        }
      },
      "/?mobile": {
        "viewport": {
          "width": 320,
          "height": 570
        },
        "filename": "mobile.html"
      },
      "/chat/": {}
    },
    "crawl": false
  },

If I will accept this PR it means that I will need to support this feature in the future and I'm not convinced this popular enough request.

SIde note: include can look like this instead (a bit more elegant from my POV):

[
  {
    "path": "/",
    "viewport": {
      "width": 1366,
      "height": 768
    }
  },
  {
    "path": "/?mobile",
    "viewport": {
      "width": 320,
      "height": 570
    },
    "filename": "mobile.html"
  },
  "/chat/"
]

Good note, I fix it.
But if you really want to make spa-snap instead react-snap, it will be a useful feature. Anyway, thank you for fast feedback, good library ;)

I do not think this is a good approach to do this. I mean you need to configure react-snap, you need to configure server router (lambda in this case), you need to configure JS router (something like react-router), and you need to keep all those things in a sync. This can work, but this is an error-prone approach. Where is react-snap intended to work more naturally - you write JS router, and everything else picked up automatically. Maybe in the future, I will reconsider this feature, but for now, I do not think this is a good idea to have this feature in core. Maybe react-snap can be refactored to make it easy to implement what you want with programmatic API

Good note, I fix it.
But if you really want to make spa-snap instead react-snap, it will be a useful feature. Anyway, thank you for fast feedback, good library ;)

Hi,

I believe Amareis is correct here, if we actually need to generate both index.html(desktop) and mobile.html(mobile) and the serving part can be handled with nginx based on user agent. We can produce snaps for both and will suffice the need of both mobile and desktop. I am trying to work on if successful would raise a PR for it.

@ankitarora05 it already done in my fork, https://github.com/Amareis/react-snap

@Amareis I am building my application through package.json rather than calling postbuild : "react-snap".
I Changed it to postbuild: "node reactSnapConfig.js"

And for serving the snapshot I am handling it at nginx based on userAgent.

//reactSnapConfig.js
const { run } = require("react-snap");

run({
source: "build",
destination: "mobile",
"viewport": {
"width": 480,
"height": 850
}
});

run({
source: "build",
destination: "desktop",
"viewport": {
"width": 1600,
"height": 1200
}
});

This did the trick for me as i have huge build system and multiple modules.

Thanks again @stereobooster for the awesome job.

Bump, I'd also like an option to set cookies when running react-snap. It'll help with customizing the site that's being crawled.

After digging through the source code, it seems like adding support for cookies is doable. All you'd have to do is call page.setCookie(...cookies) in the fetchPage function in puppeteer_utils.js.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lewisdonovan picture lewisdonovan  路  7Comments

stereobooster picture stereobooster  路  5Comments

Marcosul picture Marcosul  路  4Comments

alan345 picture alan345  路  3Comments

ShayanJavadi picture ShayanJavadi  路  5Comments