Automatically generate your own React components with plop.js

April 30, 2019 0 Comments

Automatically generate your own React components with plop.js



Do you often find yourself copying and pasting old components as templates for new components? I know I do.

Having worked with Angular before, I started to feel sad that there is no React equivalent to the Angular CLI’s generate API. For those who are unfamiliar with it, the Angular CLI offers utilities to generate components, routes, services, and pipes all from the command line.

Then I started thinking, why is there no React equivalent? It occurred to me that the React ecosystem is not as standardized as the Angular ecosystem. A new React app doesn’t ship with much. Directory structure and dependencies are largely left up to the developer. This can be nice, but the lack of uniformity amongst React applications makes tools such as React component generators a little bit harder to tackle.

After some investigation, I stumbled upon a library called plop. Plop lets you define your own parameterized code templates, which can be injected into your source code to generate boilerplate code that you would otherwise have to write yourself.

The neat thing about plop is that it provides a fairly high-level API but is simultaneously very customizable. Our imagination is the bottleneck of what can be generated with this tool. We’re not limited just to components; we can generate containers, services, hooks, reducers, actions, utilities, docs, readme’s, etc.

Before we can generate boilerplate code automatically, we need to know what boilerplate we want to generate. Over the years, I’ve come to enjoy the following structure for my React apps, so I’m going to build my generators with this structure in mind.

A few things to note about this setup:

  • components holds our reusable react components
  • pages holds single-use react components (most often rendered by routes)
  • hooks holds our custom react hooks
  • services holds stateful business logic services
  • index.js files are used to expose interfaces at the directory level
  • Styling is done via CSS Modules (ships with create-react-app)
  • Testing is done via Jest (ships with create-react-app as well)

Now that we know how we want to structure our code, we can focus on the interface of the CLI we are going to build. For the purposes of this article, we will allow the generation of components, pages, hooks, and services. To keep it simple, the only argument we will provide for each is a name.

Let’s create a new React app and configure plop.

Once we’ve run these commands, we’ll want to modify plopfile.js to include the following:

We’ll also need a component template to specify the content of our generated components. We can create this under plop-templates/Component.js.hbs (the hbs extension indicates that this is a handlebars template).

And lastly, we’ll want to add a script to our package.json to create an alias for the plop command.

Here’s the result of this configuration:

You can see when we try to generate a component on the command line, our prompt kicks in, asking the user for a component name. It then uses the value to populate the name parameter in our handlebars template. The pascalCase modifier is used to ensure name is formatted in pascal case. Here’s a full list of available template helpers.

We’re headed in the right direction, but this is still pretty wimpy. Let’s beef up our plopfile.js to provide some really cool functionality.

We’ve added several more actions. We don’t want our generated components represented as just single files, we want them represented as directories packaged with all sorts of goodies (tests, styles, etc.). We’ve added actions to create a new directory for each new component, and we’ve instructed plop to add to the directory not just our component file, but files for tests, styles, and an index file to act as an interface for the component directory.

Additionally, we’ve added a couple actions to append to an existing file, components/index.js. This file will be responsible for packaging all of our components into a neat little module so we can import them elsewhere as follows: import { Foo, Bar } from './components'.

Plop actions with type: 'append' search for a pattern within a file and inject a rendered template after the pattern match locations. For this reason, we ensure a file exists at components/index.js and then we append rendered templates at the locations of plop injection hooks within this file.

Let’s have a look at the updated templates:

In the template above, we see the aforementioned plop hooks. Note that these “hooks” are totally arbitrary, we just need something in our file that we can match against patterns defined within our plop actions.

The remainder of the templates are pretty straightforward as they don’t introduce any new concepts.

Although it’s not an issue in this example, one thing to be careful of is syntax conflicts between your template content and handlebars.

For example, if you had a template containing <div style={{ height: 100 }}/>, handlebars would incorrectly interpret the curly braces as expressions. In this case you would have to escape the curly braces as follows: <div style={{ height: 100 }}/>.

With our updated plopfile, our generator is looking pretty slick.

Now that we’ve built out our component generator, we can build generators for pages, hooks, and services as well. Building these other generators doesn’t introduce any new concepts, so I will skip ahead to show our final plopfile.

There you have it. This plopfile will let us generate components, pages, hooks, and services. The above code references many template files; I’ve decided there are too many of them to show them all in gists, but you can view them all in the react-starter-cli repository.

Here’s the final thing in action:

So, we have a super awesome and highly personalized React CLI, but where do we go next? Here are some ways you might wish to extend this plop configuration:

  • Add a prompt to component generation to specify whether the component should be a class component or a functional component
  • Instead of hardcoding components to go into a /components directory, add a prompt to dynamically input a directory where a component should be created
  • Add validation to user input (e.g., minimum length requirement)
  • Populate propTypes and/or defaultProps via prompts
  • Add generators for action types, actions, and reducers
  • Use plop with other frameworks, not just React!

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

Try it for free.

Tag cloud