- Notifications
You must be signed in to change notification settings - Fork0
📰 Painless universal pre-rendering for Webpack.
License
curology/prerender-loader
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Painless universal prerendering for Webpack. Works great withhtml-webpack-plugin.
🧐What is Prerendering?
Pre-rendering describes the process of rendering a client-side application atbuild time, producing useful static HTML that can be sent to the browserinstead of an empty bootstrapping page.
Pre-rendering is like Server-Side Rendering, just done at build time to producestatic files. Both techniques help get meaningful content onto the user'sscreen faster.
- Works entirely within Webpack
- Integrates withhtml-webpack-plugin
- Works with
webpack-dev-server
/webpack serve
- Supports both DOM and String prerendering
- Asynchronous rendering via async/await or Promises
prerender-loader
renders your web application within Webpack during builds,producing static HTML. When the loader is applied to an HTML file, it creates aDOM structure from that HTML, compiles the application, runs it within the DOMand serializes the result back to HTML.
First, installprerender-loader
as a development dependency:
npm i -D prerender-loader
In most cases, you'll want to apply the loader to yourhtml-webpack-plugin
template option:
// webpack.config.jsmodule.exports = { plugins: [ new HtmlWebpackPlugin({- template: 'index.html',+ template: '!!prerender-loader?string!index.html', // any other options you'd normally set are still supported: compile: false, inject: true }) ]}
What does all that punctuation mean? Let's break the whole loader stringdown:
In Webpack, a module identifier beginning with
!!
will bypass any configuredloaders frommodule.rules
- here we're saying "don't do anything toindex.html
except what I've defined hereThe
?string
parameter tellsprerender-loader
to output an ES moduleexporting the prerendered HTML string, rather than returning the HTML directly.Finally, everything up to the last
!
in a module identifier is the inlineloader definition (the transforms to apply to a given module). The filename ofthe module to load comes after the!
.Note: If you've already set up
html-loader
orraw-loader
to handle.html
files, you can skip both options and simply pass atemplate
value of"prerender-loader!index.html"
!
As with any loader, it is also possible to applyprerender-loader
on-the-fly:
consthtml=require('prerender-loader?!./app.html');
... or in your Webpack configuration'smodule.rules
section:
module.exports={module:{rules:[{test:'src/index.html',loader:'prerender-loader?string'}]}}
Once you haveprerender-loader
in-place, prerendering is now turned on. Duringyour build, the app will be executed, with any modifications it makes toindex.html
will be saved to disk. This is fine for the needs of many apps,but you can also take more explicit control over your prerendering: either usingthe DOM or by rendering to a String.
During prerendering, your application gets compiled and run directly underNodeJS, but within aJSDOM container so that you can use the familiar browserglobals likedocument
andwindow
.
Here's an exampleentry
module that uses DOM prerendering:
import{render}from'fancy-dom-library';importAppfrom'./app';exportdefault()=>{render(<App/>,document.body);};
In all cases, asynchronous functions and callbacks are supported:
import{mount}from'other-fancy-library';importappfrom'./app';exportdefaultasyncfunctionprerender(){letres=awaitfetch('https://example.com');letdata=awaitres.json();mount(app(data),document.getElementById('app'));}
It's also possible to export a function from your Webpack entry module, whichgives you full control over prerendering:prerender-loader
will call thefunction and its return value will be used as the static HTML. If the exportedfunction returns a Promise, it will be awaited and the resolved value will beused.
import{renderToString}from'react-dom';importAppfrom'./app';exportdefault()=>{consthtml=renderToString(<App/>);// returned HTML will be injected into <body>:returnhtml;};
In addition to DOM and String prerendering, it's also possible to use acombination of the two. If an application's Webpack entry exports a prerenderfunction that doesn't return a value, the default DOM serialization will kickin, just like in DOM prerendering. This means you can use your exportedprerender function to trigger DOM manipulation ("client-side" rendering), andthen just letprerender-loader
handle generating the static HTML for whatevergot rendered.
Here's an example that renders aPreact application and waits for DOMrendering to settle down before allowing prerender-loader to serialize thedocument to static HTML:
import{h,options}from'preact';import{renderToString}from'preact';importAppfrom'./app';// we're done when there are no renders for 50ms:constIDLE_TIMEOUT=50;exportdefault()=>newPromise(resolve=>{lettimer;// each time preact re-renders, reset our idle timer:options.debounceRendering=commit=>{clearTimeout(timer);timer=setTimeout(resolve,IDLE_TIMEOUT);commit();};// render into <body> using normal client-side rendering:render(<App/>,document.body);});
When applied to a.html
file,prerender-loader
will inject prerenderedcontent at the end of<body>
by default. If you want to place the contentsomewhere else, you can add a{{prerender}}
field:
<html><body><divid="app_root"><!-- Inject any pre-rendered HTML here: --> {{prerender}}</div></body></html>
This works well if you intend to provide a prerender function that only returnsyour application's HTML structure, not the full document's HTML.
In addition to processing.html
files, the loader can also directly pre-render.js
scripts. The only difference is that the DOM used for prerender will beinitially empty:
constprerenderedHtml=require('!prerender-loader?string!./app.js');
All options are ... optional.
Option | Type | Default | Description |
---|---|---|---|
string | boolean | false | Output a JS module exporting an HTML String instead of the HTML itself |
disabled | boolean | false | Bypass the loader entirely (but still respectoptions.string ) |
documentUrl | string | 'http://localhost' | Change the jsdom's URL (affectswindow.location ,document.URL ...) |
params | object | null | Options to pass to your prerender function |
env | object | {} | Environment variables to define when building JS for prerendering |
This is not an official Google product.
About
📰 Painless universal pre-rendering for Webpack.
Resources
License
Stars
Watchers
Forks
Packages0
Languages
- JavaScript100.0%