Amazing Razzle React SSR app with examples. Zero configuration.

Razzle is a tool that abstracts all the complex SSR logic and lets you create a great initial load experience for your users with ZERO configuration.

If you are curious about how to create your first Razzle application, what steps does it take to create Razzle app, how to implement routing with Razzle, or just see what does it capable of then join me and let’s find out.

In short, you need to run npx create-razzle-app app-name and your app is ready. You can run the npm start script and navigate to localhost:3000 to see your astonishing new application.

Now let’s see it in detail if you are ready.

Application setup

I’ve just got this React SSR application by literally running 1 command.

npx create-razzle-app razzle-react

razzle-react is my application name. Razzle will create a folder with this name and will put all the app’s code inside the folder.

After the script has finished it’s work, you will see the app with a structure like this:

How to implement routing?

Routing logic is already implemented by Razzle!

It means that you simply have to add another Route to App.js file to create a different route that would render a component.

// App.js

// ... more imports
import Home from './Home';
import OtherComponent from './OtherComponent'

const App = () => (
  <Switch>
    <Route exact path="/" component={Home} />
    <Route exact path="/other" component={OtherComponent} />
  </Switch>
);

export default App;
// OtherComponent.js
// Can be as simple as this

const OtherComponent = () => 'Hello from other component!'

If you are interested in how things work in-depth under the hood then let’s take a look at how SSR is implemented by Razzle.

If not, it’s totally fine to scroll to the next section and see what changes we can make to improve your Razzle app and make it truly remarkable.

// client.js

import App from './App';
import { BrowserRouter } from 'react-router-dom';
import React from 'react';
import { hydrate } from 'react-dom';

hydrate(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);
// server.js
// simplified version to make it more readable and short
import { StaticRouter } from 'react-router-dom';
import { renderToString } from 'react-dom/server';

app.get('/*', (req, res) => {
    const markup = renderToString(
      <StaticRouter location={req.url}>
        <App />
      </StaticRouter>
    );

    res.status(200).send(`
      <!doctype html>
         <head><!-- head content --></head>
         <html>
             <body>${markup}</body>
         </html>
    `)
}

As you can see when a browser sends any “GET” request to the server it responds with the App component rendered as a string which is wrapped to a StaticRouter.

StaticRouter is mostly used for SSR and testing purposes. I bet you have never used it in the browser. Have you?

What is does is it renders a proper component based on the location you passed as a prop to it.

So if the location is “/” then it will render a Home component, but if it is “/other” than it would render the OtherComponent. Just like a good old BrowserRouter.

As you can remember the rendered component is just an HTML string as renderToString from react-router-dom/server was used.

After the HTML was served by the server and was hydrated on the client the browser’s BrowserRouter router will handle all the routing logic from this point.

Meaning that your routing experience will be seamless and smooth.

What should you improve before your app can see the world

Have you seen this blink?

Looks far from beautiful in my opinion, let’s try to find out what’s the reason.

If you take a closer look at the response from the server you can see that there are no styles. The server sends us pure HTML markup.

In the image above, you can see the preview of how does the page looks like when it is loaded.

Then only after the client hydration styles are injected into the markup. And this is the reason for this nasty blink.

How can we overcome an issue?

mini-css-extract-plugin for Webpack and a bit of custom Razzle configuration

// razzle.config.js
// in the root of the project codebase on the same level as package.json

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    modify: config => {
      config.plugins = [
        new MiniCssExtractPlugin(),
        ...config.plugins,
      ]

      config.module.rules = [
        ...config.module.rules.filter(({ test }) => {
          if (!test || !test.test) return true
          return !test.test('.css')
        }),
        {
          test: /\.my.css$/i,
          use: [MiniCssExtractPlugin.loader, 'css-loader'],
        },
      ]
  
      return config;
    },
  };

razzle.config.js helps you to extend Webpack config due to Razzle documentation

Why do we need this plugin and custom configuration?

Our goal is to extract all the CSS written for the app’s components as a separate file during the build process so we can serve it along with HTML markup to get rid of the blink that we discussed earlier.

Now let’s see a bit closer what each piece of code does.

// just add the MiniCssExtractPlugin to the existing webpack plugins
config.plugins = [
   new MiniCssExtractPlugin(),
   ...config.plugins,
 ]
config.module.rules = [
  // remove the existing css loaders
  ...config.module.rules.filter(({ test }) => {
    if (!test || !test.test) return true
      return !test.test('.css')
    }),
    // add own css loaders
    {
      test: /\.css$/i,
      use: [MiniCssExtractPlugin.loader, 'css-loader'],
    },
  ]

This code could have been a lot cleaner if we could use the same pattern for adding the MiniCssExtractPlugin.loader as we used for plugins.

But unfortunately, I haven’t found a way to do it because MiniCssExtractPlugin.loader has to come prior to css-loader, but if you add it before all the loaders than it conflicts with the next CSS loader in the list.

I hope you will a better way to do it, but for now, this code will do the trick and it perfectly highlights the idea.

npm run start

No more blinks!

Check more information about mini-css-extract-plugin in the related Webpack documentation section.

Alternatively, you should definitely try styled-components with its ServerStyleSheet.

In short, you can wrap your component with a method call from ServerStyleSheet like this:

const html = renderToString(sheet.collectStyles(<YourApp />))
const css = sheet.getStyleTags()

But I haven’t managed to make it work. The resulting css was an empty string in my app.

If you are curious about this method you might want to check the styled-components documentation section about SSR

Conclusion

Razzle is incredibly easy to use. You can create a working SSR app with a literally single command as we’ve seen before.

It provides a great developer’s experience by providing an incredibly flexible configuration of the build process.

Besides things we’ve seen, you can add plugins to your razzle build process to add Typescript or make it Vue compatible, for example, customize babel configuration and much more.

By the way, Razzle is created by Jared Palmer the author of Formik that is used by an incredible amount of developers that brings some extra credibility for Razzle.

If you are interested in further investigation of Razzle topic than you can find more relevant information in Razzle Github repository.

If you want to spend some more time reading articles on letconst you could check my previous articles Deno vs Node.js. With code examples and sample app and Svelte with Typescript. Make it possible with Webpack

Please share your feedback about this article to letconstportal@gmail.com, request topics you would like to see highlighted on letconst, or just tell me how are you!