Scaffolding components, the easy way Simple-Scaffold
How I use Simple-scaffold to create a component boilerplate and how you can too.
#Simple Scaffold
In this article, we will look at how components can be created quicker and more robustly using Simple Scaffold, a command line application for creating multiple files, which makes it perfect for scaffolding components.
This guide is focussed on React components with CSS and stories, but Simple Scaffold can just as easily be used for creating Blade or Twig components used in PHP.
#Creating a new component
What we are going to build is a method of scaffolding components quickly by creating templates for Simple Scaffold, that allow us to create new, named components that have all of their relationships and class names generated for us.
#Folder and file structure
For this tutorial, we will be creating a component with the following structure:
/MyComponent/ (folder)
- MyComponent.tsx
- MyComponent.css
- MyComponent.stories.tsx
#The goal
The aim of this article is to create a scaffolding script that can be run from the CLI of our project to create new components in seconds.
Here is the command we are going to setup:
npm run create-component MyComponent#The end result
This is what we want to generate:
export const MyComponent = (props: {example: string}) => {
return (
<div className="my-component">
<p>{props.example}</p>
</div>
)
}
.my-component {
/* Styles go here... */
}import type { Meta, StoryObj } from '@storybook/react'
import { MyComponent } from './MyComponent'
type Story = StoryObj<typeof meta>
const meta = {
title: 'Components/MyComponent',
component: MyComponent,
parameters: {
layout: 'padded',
},
tags: ['autodocs'],
} satisfies Meta<typeof MyComponent>
export default meta;
export const Default: Story = {
args: {
example: 'This is example text',
},
}
#Using Simple scaffold
npx allows you to run any NPM package directly from the NPM registry without requiring a global installation.
#Configuration
It is possible to write most of the setup directly into the CLI, but Simple Scaffold provides a way of defining scaffolds within a JSON file, which is a much more robust way of generating scaffolding templates.
Let's create a simple-scaffold.json file in the root folder of our project:
touch simple-scaffold.json#Creating template files
I like to prefix my template folders with a period, but that is up to you, the folder name can be anything valid for your operating system.
mkdir .component-templateFinally, let's create our files, ready to start writing our templates. You will notice that each file contains a special string used by Simple Scaffold. It is written as {{name}} . This is used as a variable by Simple Scaffold and is set when we run our create-component command later on.
touch .component-template/{{name}}.tsx .component-template/{{name}}.stories.tsx .component-template/{{name}}.module.cssWith our files created, we can begin by adding our templates to simple-scaffold.json.
{
"component": {
"templates": [
".component-template/{{name}}.tsx",
".component-template/{{name}}.stories.ts",
".component-template/{{name}}.module.css"
],
"output": "src/components/{{name}}"
}
}
The component key is user-defined and could be any other name, this allows for additional scaffolds to be defined in this file in the future under a different key. (As an example, I have an additional config for creating Payload CMS Blocks).
Within our primary component key, we define another key, templates which is required by Simple Scaffold. It should be an array of paths to each of our template files.
The output value allows us to define where our components will be created. In this example, components will be added to src/components.
#Creating Components from template files
npx simple-scaffold -c simple-scaffold.json -k component ExampleComponentThis command will create a new component named ExampleComponent. It will generate the folder and files within. Let's have a quick look at the command arguments to understand what we have written:
npx simple-scaffoldis what executes the command.- The
-cflag is shorthand for--configand requires a filename to load our configuration from. - The
-kflag is short for--keyand this references our primary keycomponentfrom simple-scaffold.json and tells Simple Scaffold which configuration to use. - The final argument to add is the desired name of our component. As we are working with React Components, I have used PascalCase for the name, but this is not enforced and Simple Scaffold is naming convention agnostic.
Running the command above will create a new folder called ExampleComponent that contains the following files:
- ExampleComponent.tsx
- ExampleComponent.styles.css
- ExampleComponent.stories.ts
#Writing component file scaffolds
So now we have gone through how to create basic component files using Simple Scaffold, let's take a deeper look at the powerful templating and replacement engine capabilities of Simple Scaffold.
The {{ name }} Template string works within template files and allows us to set up component exports, CSS rules and even generate Storybook stories for new components that are all preconfigured to use the correct names. Simple Scaffold has some great documentation on using {{ name }} effectively and they provide some useful helpers for instances where you need to reference the component name in a different case, such as when you need to use PascalCase to name a component but your CSS naming convention uses hyphenation and lowercase class names.
#Creating the React component template
import styles from './{{ name }}.css'
export const {{ name }} = (props: {example: string}) => {
return (
<div className="{{ hyphenCase name }}">
<p>{props.example}</p>
</div>
)
}In this example, we set up a very basic component. Any instances of {{ name }} are replaced with the Component Name specified when running our scaffolding command. Note that the className is defined as {{ hyphenCase name }}. Assuming that our component is named ExampleComponent, then {{ hyphenCase name }} will return example-component
#Result
This template will generate the following output:
import styles from './ExampleComponent.css'
export const ExampleComponent = (props: {example: string}) => {
return (
<div className="example-component">
<p>{props.example}</p>
</div>
)
}#Creating the CSS template
.{{ pascalCase name }} {
/* Styles go here... */
}As above, any instances of {{ name }} is replaced with the Component name specified when running our scaffolding command. As we defined our React component className to use pascalCase we will do the same for the CSS rule by once again using {{ pascalCase name }}
#Result
This template will generate the following output:
.example-component {
/* Styles go here... */
}#Creating the Storybook template
This is an opinionated StorybookJS story scaffold, based on my own setup which highlights how {{ name }} can be interspersed in a larger and more complex file. Not how it even works defining imports, despite the additional curly braces.
import type { Meta, StoryObj } from '@storybook/react'
import { {{ name }} } from './{{ name }}'
type Story = StoryObj<typeof meta>
const meta = {
title: 'Components/{{ name }}',
component: {{ name }},
parameters: {
layout: 'padded',
},
tags: ['autodocs'],
} satisfies Meta<typeof {{ name }}>
export default meta;
export const Default: Story = {
args: {
example: 'This is example text',
},
}
#Result
This template will generate the following output:
import type { Meta, StoryObj } from '@storybook/react'
import { ExampleComponent } from './ExampleComponent'
type Story = StoryObj<typeof meta>
const meta = {
title: 'Components/ExampleComponent',
component: ExampleComponent,
parameters: {
layout: 'padded',
},
tags: ['autodocs'],
} satisfies Meta<typeof ExampleComponent>
export default meta;
export const Default: Story = {
args: {
example: 'This is example text',
},
}#Conclusion
Following this guide should give you a good foundational understanding of what simple Scaffold is and how it can be used to scaffold components quickly and introduce a set of standards for how components should be created within your project. Simple Scaffold is very flexible and language agnostic so it can be used for all kinds of project setups in various languages.
If you have any thoughts or feedback on the article, you can contact me on Bluesky.