Making examples: Displaying code along its output with webpack

Momtchil Momtchev
4 min readMar 15, 2021

Recently I had to make some examples for using my new cartography library for React. For every example, I wanted to be able to display both the component output and the code itself, like this:

(the code loading the examples discussed here is in https://github.com/mmomtchev/rlayers/blob/master/examples/App.tsx)

As some of the examples were complex and constantly evolving, it was all natural to assume that this was to be all automatic with no maintenance required after the initial setup.

My first reflex was to use React itself, trying to leverage the JSX and to transform it to HTML. Indeed, there are several NPM packages that do exactly this —I tried both jsx-to-string and react-element-to-jsx-string.

Although both worked, this approach — using the parsed code — had a number of shortcomings:

  • The original formatting was lost;
  • All expressions were replaced by their scalar results.

This first was easily overcome by using prettier, which I also kept for my final implementation. prettier, which is frequently used alongside eslint for enforcing code formatting in a project, has also a browser version that can be embedded in a front-end project.

The second drawback however had no easy solution.

After some searching, I came up with an alternative solution that was not dependent on React. Use webpack to import the code twice, once as a code, and once as a string, then use prettier and a syntax highlighter — the two most used ones are highlight.js and prismjs. highlight.js is the slightly more popular one and has more styles, but prismjs is the only one with good support for JSX and TSX — so if you are illustrating React, it is the only solution.

If you are using React with webpack, you probably already have a setup for loading the JSX or TSX files with it. In order to load a React component you do:

This statement will make webpack apply its default loader, depending on the filetype of example1. But you also have the possibility to manually override the loader used:

Note that this time the extension has to be explicitly specified, as webpack cannot guess it anymore. raw-loader does not come packaged by default, it is available by doing:

npm -save-dev install raw-loader

It is a loader that simply loads the file contents as a string in the variable.

Now if we combine this with prettier and highlighter.js/prismjs we are all set up. prettier can be somewhat tricky to use in a browser and the documentation is quite misleading. Here is the right way to do it:

Then pass the formatted output to the highlighter. Just be aware that prettier and the syntax highlighter will come with a very hefty price on the size of your bundle — having both of them will add a few megabytes. There is also react-syntax-highlighter which is a React component for displaying the results of prettier and highlight.js and can be a very good choice if your examples are part of a React application.

If you have many examples, you might also automatically lazy-load them with a single loop using dynamic imports. The following supposes you are already using dynamic imports with your webpack project — if not there are many excellent tutorials available:

This will get you, for each example, two Promises that will resolve with the component as a function and its code as a string. The webpack magic comment ensures that the browser will start loading those elements as soon as the entry point bundle has finished loading. Please note that special way that the filenames are specified — webpack supports dynamic imports but it needs to be aware of the files that will be packaged in the bundle at compile-time. In order to make sure that all eventual import requests will get fulfilled, it will parse your string argument to import() and it will replace all variables with a wildcard ‘*’ — then it will package all files that match that pattern. The best way to take advantage of this mechanism is to arrange your project structure in a way that makes it possible to match the examples with a file glob pattern.

With this setup, adding a new example will be as simple as adding a new file in the right folder.

Now there is one additional optimization possible. Including both prettier and highlight.js or prismjs in the final bundle comes with a really large increase in size. It also allows us to reformat the code on the fly, eventually re-adapting it for a responsive layout. However, if you are always rendering everything the same way, why not apply it when bundling and get rid of those two very heavy packages in the client code?

We will make them run in a custom webpack loader:

Let’s call this loader jsx-loader.ts. It loads the code and then applies both prettier and prismjs producing plain HTML. Then we will call it like this:

We are chaining our custom loader to the HTML loader. It is very important to disable the HTML minification, otherwise all the work of prettier will be undone as the code will be pre-formatted inside a <pre> tag. Now the Promise will resolve with a plain string containing the preformatted TSX code in HTML. Voila, our bundle is down 5MB while the output stays the same!

You can browse the final result at

And the code generating that page at

Happy illustrating!

March 2024 Update: For an example that works with the recently published prettier@3, check https://github.com/mmomtchev/ol-mbtiles/blob/main/examples/prettier-loader.cjs

--

--