Building Design System UI kits for large frontend applications
Design systems have become more crucial for building large scale frontend applications. They are used to build reusable components which help us to build, scale and maintain the frontend applications.
Building complex frontend architecture requires starting with small foundation blocks. This foundation helps us build the application’s castle. Reusable components are just like Lego blocks which can be used to build various products.
What is a Design System ?
A design system is a complete guide to the UI components shared between multiple projects. It is also the source of truth for an organization’s shared UI components. It focuses on core workflows that promote adoption and simplify maintenance.
In this article we will be discussing how we can build reusable components by using this workflow.
Pre-requisite: Node (version 14 or above).
Let’s create a simple React application using the below command.
npx create-react-app starter-design-systems --template typescript
This will create a React starter project with app name starter-design-system
. Next open this project in your IDE (I’m using Visual Studio Code).
Now let’s install the following packages to get started with the design system. Go to the terminal for the above project. The below commands should install the necessary packages (Installation may take some time).
#Install the React bootstrap frontend framework
npm install react-bootstrap bootstrap#Installs the testing packages
npm install --save-dev react-test-renderer @types/react-test-renderer#Installs and initializes the storybook
npx storybook init
If your current node version is below 14.17, you will need an additional package to run the jest commands.
#Installs the typed definitions for react-test-renderer
npm i --save-dev jest-watch-typeahead
Initializing the storybook bootstraps the related folders and files into the project. It creates a .storybook
folder in the root of the project and a stories
folder in src/
, which will contain all the component’s story files.
Also import the bootstrap.css file inside the src/index.tsx
file and .storybook/preview.js
file. This will add all the bootstrap css and styles to the project and storybook stories.
import 'bootstrap/dist/css/bootstrap.css';
Next let’s run the storybook
npm run storybook
This will open the storybook page in Google Chrome which shows the list of reusable components. By default, Storybook gives three new components: Button, Header and Page. These components can be removed if not needed.
Let’s create an Alert component which can give an alert message and also change the color based on the type of alert.
Create a components
folder in the src/
folder. Inside this folder create an alert
folder. Inside this folder create a file alert.tsx
src -> components -> alert -> alert.tsx
Copy the following code for the alert component in alert.tsx
.
import { Alert as ReactAlert } from 'react-bootstrap'import { Variant } from 'react-bootstrap/esm/types';interface Props {variant?: Variant;message?: string;}const Alert = ({ variant, message } : Props) => {return (<ReactAlert variant={variant}> {message} </ReactAlert>)}Alert.defaultProps = {variant: 'success',message: 'This is an alert message'}export default Alert;
Now let’s create a story for this component in order to view the component in the storybook interface. Create a file alert.stories.tsx
inside the src/stories
folder with the following code.
import { ComponentStory, ComponentMeta } from '@storybook/react';import Alert from '../components/alert/alert';enum VariantType {Primary = "primary",Secondary = "secondary",Danger = "danger",Success = "success",}export default {title: 'Example/Alert',component: Alert,argTypes: {variant: {control: { type: "select", options: VariantType },description: "Type of alert",defaultValue:"success",},message: {control: 'text',description: 'Enter the alert message',defaultValue: 'This is an alert message'}}} as ComponentMeta<typeof Alert>;const Template: ComponentStory<typeof Alert> = ({variant, message}) => <Alert variant={variant} message={message} />;export const Default = Template.bind({});Default.args = {
variant: "success"
}
You can read more about storybook configuration here.
If you save these files and refresh the running storybook interface in the browser you should be seeing an Alert component in the component lists. If you don’t see your new component in the Storybook, then stop and run the storybook command.
This will also add the documentation for the component in Docs tab which can be used as a reference while using the component in the application.
Storybook UI can help us isolate the building of components in parallel with the Application UI. A large frontend application may have many different types of reusable components.
We can also create stories for these components in the Storybook UI by creating .stories.mdx
inside the src/stories/
folder. This will also add the Markdown syntax along with .jsx
syntax. You can find more info about the .mdx
documentation here.
Next we will need to create snapshot tests. Creating a snapshot of the component can make sure it does not change unexpectedly.
Create a new file alert.spec.tsx
inside the alert component folder and add the following code which will create a snapshot of the alert component.
import renderer from 'react-test-renderer';import Alert from '../alert/alert';it('renders correctly', () => {const tree = renderer.create(<Alert variant='success' message='This is an alert message'/>).toJSON();expect(tree).toMatchSnapshot();});
src->components->alert->alert.spec.tsx
Run npm run test
to generate a snapshot for the alert component. This will create a __snapshots__ folder in which the .snap
file is generated.
You can also add other testcases to this .spec
file based on the component’s functionality test coverage.
Now this component can be used anywhere else in the application. Any change to this component will fail the npm test. These snapshots must be updated based on the changes so that any unexpected changes can be recognized early.
Final Thoughts
Creating a complex frontend application requires a lot of planning, design and development. Building a design system in the initial phases of software development lifecycle can help us to reach the deliverables with much ease and precision.
References: