Generate your own React Component using CLI by using Plop.js
Tags: react, npm, reactnative
Published: 2020-04-07 11:28:58

Those who have previously worked in Angular may be noticed that the React ecosystem is not standardized as an Angular ecosystem. A new React app has nothing much. The directory and dependencies structures can be made as our own wish. That could be good, but that rise lack of uniformity amongst React developers.

We don't have a CLI command similar to [angular-cli](https://angular.io/) to generate a new component, the research to overcome this helped me to find [plop.js](https://plopjs.com/documentation/#getting-started).

**What is Plop?**
Micro-generator framework that makes it easy for an entire team to create files with a level of uniformity.

Let's Start coding...

### Setting up your app

First, let’s create a new react app using [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app)

`npx create-react-app my-app`

Once the app is successfully created, now change your working directory by running below command.

`cd my-app`

### Installing plop.js

Install plop as a dev-dependency

`npm install --save-dev plop`

### Setting up the project structure

We need to decide, what boilerplate you want to generate. Over the last few projects, I'm using the following structure for my React app.




### Configuring plop

create a `plopfile.js` in your root app folder and add the below code.

```javascript
const { readdirSync } = require("fs");

const getDirectories = (source) =>
	readdirSync(source, { withFileTypes: true })
		.filter((dirent) => dirent.isDirectory())
		.map((dirent) => {
			return { name: dirent.name, value: dirent.name };
		});

module.exports = (plop) => {
	let directories = getDirectories("src/pages");
	plop.setGenerator("component", {
		description: "Create a component",
		// User input prompts provided as arguments to the template
		prompts: [
			{
				// Raw text input
				type: "input",
				// Variable name for this input
				name: "name",
				// Prompt to display on command line
				message: "What is your component name?",
			},
			{
				// Raw text input
				type: "confirm",
				// Variable name for this input
				name: "isCommon",
				// Prompt to display on command line
				message: "Is it common component?",
			},
			{
				when: function (response) {
					return response.isCommon === false;
				},
				// Raw text input
				type: "list",
				// Variable name for this input
				name: "container",
				// Prompt to display on command line
				message: "Choose container?",
				choices: directories,
			},
		],
		actions: (data) => {
			const path = data.isCommon ? "src/common/" : "src/pages/" + data.container + "/";

			let actions = data.isCommon
				? [
						{
							// Add a new file
							type: "add",
							// Path for the new file
							path: path + "{{pascalCase name}}/{{pascalCase name}}.js",
							// Handlebars template used to generate content of new file
							templateFile: "plop-templates/Component/Component.js.hbs",
						},
						{
							type: "add",
							path: path + "{{pascalCase name}}/index.js",
							templateFile: "plop-templates/Component/index.js.hbs",
						},
						{
							type: "add",
							path: path + "{{pascalCase name}}/{{pascalCase name}}.styled.js",
							templateFile: "plop-templates/Component/styled.js.hbs",
						},
						{
							type: "add",
							path: path + "{{pascalCase name}}/{{pascalCase name}}.stories.js",
							templateFile: "plop-templates/Component/stories.js.hbs",
						},
				  ]
				: [
						{
							// Add a new file
							type: "add",
							// Path for the new file
							path: path + "{{pascalCase name}}/{{pascalCase name}}.js",
							// Handlebars template used to generate content of new file
							templateFile: "plop-templates/Component/Component.js.hbs",
						},
						{
							type: "add",
							path: path + "{{pascalCase name}}/index.js",
							templateFile: "plop-templates/Component/index.js.hbs",
						},
						{
							type: "add",
							path: path + "{{pascalCase name}}/{{pascalCase name}}.styled.js",
							templateFile: "plop-templates/Component/styled.js.hbs",
						},
				  ];
			return actions;
		},
	});

	plop.setGenerator("page", {
		description: "Create a page",
		// User input prompts provided as arguments to the template
		prompts: [
			{
				// Raw text input
				type: "input",
				// Variable name for this input
				name: "name",
				// Prompt to display on command line
				message: "What is your page name?",
			},
		],
		actions: [
			{
				// Add a new file
				type: "add",
				// Path for the new file
				path: "src/pages/{{pascalCase name}}/{{pascalCase name}}.js",
				// Handlebars template used to generate content of new file
				templateFile: "plop-templates/Component/Component.js.hbs",
			},
			{
				type: "add",
				path: "src/pages/{{pascalCase name}}/index.js",
				templateFile: "plop-templates/Component/index.js.hbs",
			},
			{
				type: "add",
				path: "src/pages/{{pascalCase name}}/{{pascalCase name}}.styled.js",
				templateFile: "plop-templates/Component/styled.js.hbs",
			},
		],
	});
};

```

### Creating plop-templates

We need to create a plop-templates so that plop.js generates the new components based on that template.

Inside your root app folder create a new folder called plop-templates and create templates with `.hbs` extension indicates that this is a [Handlebars.js](https://handlebarsjs.com/) template.

For my folder structure, I have four template files as follows:
##### 1. Functional component

![Functional Component](https://d3ep2kbadr6005.cloudfront.net/krmblog/component.hbs.png "Functional Component")

##### 2. Styled Component

![Functional Component](https://d3ep2kbadr6005.cloudfront.net/krmblog/styled.hbs.png "Functional Component")

##### 3. Storybook

![Functional Component](https://d3ep2kbadr6005.cloudfront.net/krmblog/stories.hbs.png "Functional Component")

##### 4. index.js

![Functional Component](https://d3ep2kbadr6005.cloudfront.net/krmblog/index.hbs.png "Functional Component")


### Adding script to package.json

And lastly, we’ll want to add a script to our `package.json` to create an alias for the plop command.
```json 
"generate": "plop"
```
With this, our plop setup is complete now open your terminal and run `npm run generate`.

![npm run generate](https://d3ep2kbadr6005.cloudfront.net/krmblog/generate.png "npm run generate")

It prompts a question with two options `component` or `page`

If you choose `component`. Then,

![npm run generate | component](https://d3ep2kbadr6005.cloudfront.net/krmblog/myComponent yes.png "npm run generate | component")

After entering the component name and hitting enter key, it will prompt another question asking whether it is a common component or not. If you choose common, then the component will be generated under the common folder, else it maps the page folder, from where you could generate the component inside the respective page component.

![generated component](https://d3ep2kbadr6005.cloudfront.net/krmblog/component generation.png "generated component")

If you were chosen `page` instead of `component`, then the resulting will be:

![generated pagecomponent](https://d3ep2kbadr6005.cloudfront.net/krmblog/Page.png "generated page component")

Now that we’ve built out our component generator, we can build generators for Redux, Hooks, and services as well.

>_Thanks 🙌 for reading and happy coding!_