DEV Community

Cover image for Design Systems in Action: Building a Reusable Component Library with React
Joshua Wasike
Joshua Wasike

Posted on

Design Systems in Action: Building a Reusable Component Library with React

Creating a scalable design system is essential for maintaining consistency, improving collaboration, and enhancing the efficiency of development teams. This article will guide you through the process of building a reusable component library with React, integrating with Storybook for documentation, and ensuring consistency across projects.

A design system is a collection of reusable components, guided by clear standards, that can be assembled together to build any number of applications. Building a design system with React allows you to create a scalable, maintainable, and consistent user interface (UI) across your projects. In this article, we will cover:
Setting up a React project
Creating reusable components
Integrating with Storybook for documentation
Ensuring design consistency
Best practices for maintaining a scalable design system

Setting Up a React Project

Prerequisites

  • Node.js and npm installed on your machine
  • Basic knowledge of React

Project Initialization

Start by creating a new React project using Create React App. This will set up a React environment with a lot of boilerplate code already configured, which saves time and ensures a standard structure.


npx create-react-app design-system
cd design-system
Enter fullscreen mode Exit fullscreen mode

Install necessary dependencies for your component library. We will use styled-components for styling, which allows us to write actual CSS code to style our components.

npm install --save styled-components
Enter fullscreen mode Exit fullscreen mode

Creating Reusable Components

Components are the building blocks of a design system. Let’s create a simple button component that can be reused across different projects.

Button Component

First, create a new directory for your components inside the src folder. This helps in organizing the codebase.

mkdir src/components
Enter fullscreen mode Exit fullscreen mode

Next, create a Button.js file inside the components directory. This file will define our button component.

// src/components/Button.js
import React from 'react';
import styled from 'styled-components';

// Define the styled button using styled-components
const StyledButton = styled.button`
  background-color: ${(props) => props.primary ? props.theme.colors.primary : props.theme.colors.secondary};
  border: none;
  color: white;
  padding: ${(props) => props.theme.spacing.medium};
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin: 4px 2px;
  cursor: pointer;
  border-radius: 4px;
`;

// Define the Button component that uses the styled button
const Button = ({ primary, children, ...props }) => {
  return (
    <StyledButton primary={primary} {...props}>
      {children}
    </StyledButton>
  );
};

export default Button;
Enter fullscreen mode Exit fullscreen mode

In this code:

  • We use styled-components to create a styled button. The StyledButton component uses props to dynamically change its styles based on whether it is a primary button or not.
  • The Button component is a functional React component that accepts primary, children, and other props. It renders the StyledButton with the passed props.

Usage

To use the button component, import it into your main App.js file. This allows you to use the button component throughout your application.

// src/App.js
import React from 'react';
import Button from './components/Button';

function App() {
  return (
    <div>
      <Button primary>Primary Button</Button>
      <Button>Secondary Button</Button>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In this example:

  • We import the Button component from our components directory.

  • We use the Button component twice with different props to render a primary and a secondary button.

Integrating with Storybook for Documentation

Storybook is an open-source tool for developing UI components in isolation. It helps you document and test your components interactively.

Installing Storybook

Install Storybook in your project using the following command. This command initializes Storybook and adds it to your project.

npx sb init
Enter fullscreen mode Exit fullscreen mode

Creating Stories

Create a Button.stories.js file inside the components directory. This file will contain the stories for the button component.

// src/components/Button.stories.js
import React from 'react';
import Button from './Button';

// Define the default export with title and component properties
export default {
  title: 'Button',
  component: Button,
};

// Define different stories for the button component
export const Primary = () => <Button primary>Primary Button</Button>;
export const Secondary = () => <Button>Secondary Button</Button>;
Enter fullscreen mode Exit fullscreen mode

In this code:

  • The default export contains metadata about the component, including its title and the component itself.
  • We define two stories: Primary and Secondary. Each story is a function that returns the button component with different props. Run Storybook using the following command. This will start a development server and open your default browser to view the Storybook interface.
npm run storybook
Enter fullscreen mode Exit fullscreen mode

Ensuring Design Consistency

Consistency is key to a successful design system. Here are some tips to ensure your components remain consistent.

Theming

Use theming to ensure consistent styling across your components. Create a theme.js file to define your theme.

// src/theme.js
export const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
  },
  spacing: {
    small: '8px',
    medium: '16px',
    large: '24px',
  },
};
Enter fullscreen mode Exit fullscreen mode

Wrap your application with a ThemeProvider to apply the theme globally.

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { ThemeProvider } from 'styled-components';
import { theme } from './theme';
import App from './App';

ReactDOM.render(
  <ThemeProvider theme={theme}>
    <App />
  </ThemeProvider>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Update the button component to use the theme. We use the theme values for background color and padding.

// src/components/Button.js
import React from 'react';
import styled, { withTheme } from 'styled-components';

// Define the styled button using styled-components and theme values
const StyledButton = styled.button`
  background-color: ${(props) => props.primary ? props.theme.colors.primary : props.theme.colors.secondary};
  border: none;
  color: white;
  padding: ${(props) => props.theme.spacing.medium};
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin: 4px 2px;
  cursor: pointer;
  border-radius: 4px;
`;

// Define the Button component that uses the styled button
const Button = ({ primary, children, ...props }) => {
  return (
    <StyledButton primary={primary} {...props}>
      {children}
    </StyledButton>
  );
};

export default withTheme(Button);
Enter fullscreen mode Exit fullscreen mode

In this code:

  • The StyledButton now uses theme values for its background color and padding.
  • The withTheme higher-order component is used to provide the theme to the button component.

Design Tokens

Design tokens are a way to define and manage the visual properties of your design system. They provide a single source of truth for design values like colors, typography, spacing, etc. Create a tokens.js file to define your design tokens.

// src/tokens.js
export const tokens = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
  },
  spacing: {
    small: '8px',
    medium: '16px',
    large: '24px',
  },
  typography: {
    fontSize: '16px',
    fontWeight: 'bold',
  },
};
Enter fullscreen mode Exit fullscreen mode

Best Practices for Maintaining a Scalable Design System

Documentation

Document every component in your design system. Use Storybook to create detailed stories for each component, showcasing different states and variations.

Versioning

Use version control to manage changes in your design system. Semantic versioning helps in tracking changes and ensuring backward compatibility.

Testing

Write unit tests for your components to ensure they behave as expected. Use a testing framework like Jest and a testing utility like React Testing Library.

Install the necessary testing libraries:

npm install --save-dev jest @testing-library/react
Enter fullscreen mode Exit fullscreen mode

Create a test for the button component:

// src/components/Button.test.js
import React from 'react';
import { render } from '@testing-library/react';
import Button from './Button';

test('renders the button component', () => {
  const { getByText } = render(<Button>Click me</Button>);
  const buttonElement = getByText(/Click me/i);
  expect(buttonElement).toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

Run the tests:

npm test
Enter fullscreen mode Exit fullscreen mode

Accessibility

Ensure your components are accessible. Use tools like Axe or Lighthouse to audit your components for accessibility issues. For example, you can use the following code snippet to test for accessibility issues in your button component.

// src/components/Button.test.js
import React from 'react';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import Button from './Button';

test('should have no a11y violations', async () => {
  const { container } = render(<Button>Click me</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

Building a reusable component library with React involves creating a scalable design system, integrating with Storybook for documentation, and ensuring design consistency across projects. By following best practices and leveraging tools like styled-components and theming, you can create a maintainable and efficient design system that enhances the development workflow.

Top comments (0)