Scaffolding components, the easy way Simple-Scaffold

How I use Simple-scaffold to create a component boilerplate and how you can too.

An image of a simple scaffolding command script.

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.

It is language agnostic and 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:

MyComponent.tsx
export const MyComponent = (props: {example: string}) => {
  return (
     <div className="my-component">
        <p>{props.example}</p>
      </div>
  )
}
MyComponent.css
.my-component {
  /* Styles go here... */
}
MyComponent.stories.ts
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-template

Finally, 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.css

With our files created, we can begin by adding our templates to simple-scaffold.json.

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 ExampleComponent

This 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-scaffold is what executes the command.
  • The -c flag is shorthand for --config and requires a filename to load our configuration from.
  • The -k flag is short for --key and this references our primary key component from 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

{{name}}.tsx (Simple scaffold 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:

ExampleComponent.tsx
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

{{name}}.css (Simple scaffold 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:

ExampleComponent.css
.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.

{{name}}.stories.tsx
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:

ExampleComponent.stories.tsx
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.