As developers, we often find ourselves creating similar file structures repeatedly when working on React or Next.js projects. For instance, when building a reusable component, you might create the following files:
.tsx file for the component.
.test.tsx file for unit tests.
.stories.tsx file for documenting the component in Storybook.
While this structure is essential for maintainability and collaboration, creating these files manually every time can be tedious and error-prone. That’s where a Custom Component Generator comes in handy.
In this article, we’ll explore how a custom generator can:
- Boost consistency across your project.
- Save time during development.
- Eliminate repetitive work by automating component scaffolding.
Why Use a Custom Component Generator?
Enforce Consistency
Having a standard file structure ensures your project is easy to navigate, especially in teams. A generator enforces this consistency by automatically creating files with the correct naming conventions and boilerplate code.Save Time
Manual file creation eats up valuable time that could be spent writing actual business logic or debugging issues. A generator automates this, allowing you to focus on building features.Eliminate Errors
Human error is inevitable when creating files manually. A typo in a filename or missed boilerplate code can cause unnecessary bugs. A generator ensures all files are created correctly every time.Scalability
As your project grows, managing components becomes challenging. Using a generator ensures new components adhere to the same patterns, making the project scalable and maintainable.
Plop.js: Your Go-To Tool for File Generation
There are several tools for generating files, but Plop.js stands out because:
- It’s lightweight and easy to set up.
- It integrates well with JavaScript/TypeScript projects.
- It supports templating with Handlebars, making it flexible.
Build a Custom Component Generator
Step 1: Install Plop.js
Add Plop.js as a development dependency:
npm install --save-dev plop
Step 2: Define a Generator in plopfile.js
Create a plopfile.js at the root of your project. This file defines how the generator works. For example:
module.exports = function (plop) {
plop.setGenerator('component', {
description: 'Generate a React component with TSX, test, and Storybook files',
prompts: [
{
type: 'input',
name: 'name',
message: 'Enter the component name:',
},
],
actions: [
{
type: 'add',
path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.tsx',
templateFile: 'plop-templates/component.tsx.hbs',
},
{
type: 'add',
path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.test.tsx',
templateFile: 'plop-templates/component.test.tsx.hbs',
},
{
type: 'add',
path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.stories.tsx',
templateFile: 'plop-templates/component.stories.tsx.hbs',
},
],
});
};
Step 3: Create Templates
Create a directory plop-templates at the root of your project. Inside, add templates for the component, test, and story files:
Component Template (component.tsx.hbs):
import React from 'react';
interface {{pascalCase name}}Props {
// Define your props here
}
const {{pascalCase name}}: React.FC<{{pascalCase name}}Props> = (props) => {
return <div>{{pascalCase name}} works!</div>;
};
export default {{pascalCase name}};
Test Template (component.test.tsx.hbs):
import React from 'react';
import { render, screen } from '@testing-library/react';
import {{pascalCase name}} from './{{pascalCase name}}';
describe('{{pascalCase name}}', () => {
it('renders without crashing', () => {
render(<{{pascalCase name}} />);
expect(screen.getByText('{{pascalCase name}} works!')).toBeInTheDocument();
});
});
Storybook Template (component.stories.tsx.hbs):
import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import {{pascalCase name}} from './{{pascalCase name}}';
export default {
title: 'Components/{{pascalCase name}}',
component: {{pascalCase name}},
} as ComponentMeta<typeof {{pascalCase name}}>;
const Template: ComponentStory<typeof {{pascalCase name}}> = (args) => <{{pascalCase name}} {...args} />;
export const Default = Template.bind({});
Default.args = {};
Step 4: Run the Generator
Run the following command to generate a component:
npx plop component
You’ll be prompted to enter the component name. For example, if you type Button, the following files will be created:
src/components/Button/Button.tsx
src/components/Button/Button.test.tsx
src/components/Button/Button.stories.tsx
Step 5: Automate with NPM Scripts
To make the process even easier, add an npm script to your package.json:
"scripts": {
"generate": "plop component"
}
Now, you can run the generator with:
npm run generate
Extending the Generator
Plop.js is highly flexible. Here are some ways you can extend it:
- 1. Add Custom Prompts: Ask for additional details like component props or folder structure.
- 2. Support Style Files: Automatically generate a .module.css or .scss file for the component.
- 3. Set Up with Context/Redux: Include boilerplate for managing state.
- 4. Preconfigure Dependencies: Add imports for common libraries like Material UI, Tailwind CSS, or custom hooks.
Key Benefits of Using a Custom Component Generator
Team Alignment
A generator enforces your team’s coding standards. New team members can quickly adapt to the project structure, reducing onboarding time.Scalability
As your project grows, maintaining consistency across hundreds of components becomes easy.Productivity
Focus on solving complex problems instead of setting up boilerplate files.Maintainability
Standardized components are easier to test, debug, and extend.
Conclusion
A custom component generator is a small but powerful addition to your workflow that can make a big impact on your productivity and project quality. By leveraging tools like Plop.js, you can enforce standards, save time, and ensure scalability across your Next.js projects.
So why not try it out in your project today? Happy coding! 🎉
Got ideas or questions? Let me know in the comments. And if you found this post helpful, share it with your fellow developers! 🚀
Top comments (0)