Isomorphic app with riotjS in simple steps
Once we realized what great powers can a Single Page app bring to our web UX, a majority of web community have started shifting towards the same. Single Page apps not only bring in awesome UX(if done right), it can also help you scale as you can do minimal server side rendering and the rest of the app rendering can seamlessly happen on the client’s device. But while all of this awesomeness started becoming mainstream and got more famous among the devs we started realizing that it can hurt us back too.
- It started hampering SEO for our web apps, cuz some crawlers saw blank page and didn’t downloaded/execute your jS bundles.
- It required parsing huge chunks of jS before displaying anything on screen.
Hence we started doing server side rendering in a smarter way. Every time someone lands on the web app we give them a server side rendered app and hydrate it with the JavaScript. Later making it behave like a simple page app from that point of time.
Now a lot of great demo are there for how to do this react or a lot of other good frameworks and hence I decided to pull of the same for riot jS.
So I’ll try to build this with isomorphic rendering. We’ll throw a few necessary npm modules that’ll help us build the same.
So the main modules be
- Riot jS because… obviously
- riot-tagrouter: this is a declarative router for riotjS which lets you take advantage of PRPL pattern out of the box
- Webpack: this rad module bundler lets you take advantage of the code splitting (apart from all its mainstream module bundling stuff)
- riot-ssr: this module waits for you while you perform asynchronous task while rendering your code on server.
Now let’s begin taking core work with our code, we’ll break this task into some really basic tasks
Step 1.) Write webpack config for vendor jS and app jS bundle.
These bundles will contain all the core jS of the web app(riot, riot-tagrouter, header and nav-bar).
We are splitting vendor and app into 2 main bundles because these are 2 rarely changing bundles but still can change at different paces e.g. I don’t expect to update my dependencies of ‘riot’ and ‘riot-tagrouter’ for quite sometime, and similarly header and nav-bar may change quite seldom as compared to other components but still faster than riot(feel free to modify this strategy as per your choice).
Step 2.) Write route based entry point config for every route.
These routes can be lazy loaded once our main route is rendered and interactive. Our app has a home page, listing page, and a product page. All these three routes will generate separate jS bundles and will be loaded separately.
So now we have added entry points for every route(home, browse, product, app and vendor) and thus will generate different bundles for these routes. these bundles will be lazy loaded after 1st route is rendered on the screen for the user.
Step 3.) Declare routes of the app using riot-tagrouter.
We’ll create a new tag which will contain our router logic and declare these routes such that all of these should be lazy loaded
Be wary of the fact that while declaring lazyloaded routes you have to declare a tag that contains the ‘router’ tag, this is mostly because of how nested tags are mounted in riot jS. The functions passed as ‘opts’ to ‘yielded’ tags still live in parent’s scope hence {this.parent.loadHome}
Step 4.) Declare express route to serve bare minimum html with js and css to incoming requests.
This will build you a client side rendering SPA which would work perfectly fine but browser would show a blank white page for a long time till it downloads the js for this route and parses it to display the page.
This is one serious problem prevalent with web at this very time, the user is staring at a blank page while the web app downloads current route jS file and render something meaningful to the user.
Step 5.) Implement server side rendering for the routes, so that all the first requests for the clients have really fast first meaningful paint.
Before Server side rendering it took some 420ms on a 3G connection and had to parse about 103ms of jS to show you your first meaningful paint
After Server side rendering first meaningful paint came down by about 140ms and only 28.4 ms of jS had to be parsed.
Now our app is rendering the markup on on server too, check the view source of this page. It also makes sure that crawlers are getting the exact info what your users must be looking at.
Step 6.) Parallel hydration of your app.
Though we have a pretty decent progress at our hand, there is a small thing that we can improve something here. At this point of time we get our first pieces rendered on server and then LATER jS is downloaded, parsed and makes it interactive, till then user might be looking at the something at his phone and trying to interact with it but would be helpless as jS might still be downloading(better explanation here). To fix this we’ll add a small <link rel=‘preload’ ….> for the current route’s jS. This will start downloading the necessary jS from very beginning in parallel without blocking our rendering. These jS files we’ll use at the bottom of the page which would help us minimize the non-interactive time a little bit.
Demo: https://riot-shop.herokuapp.com
Hope you enjoyed reading.
with ❤ for web