Building a React component library with styled-components: Input Field

Fionna Chan
6 min readMar 17, 2019

--

The TextField component we are going to create as shown on my CodePen demo / GitHub demo

From simple .css files to sassy .scss files, and to the CSS in JS styled-components, web developers have come a long way in styling the web.

Building a component library with React doesn’t mean you must include styled-components. You can still use plain CSS, or SCSS files that would be compiled into CSS, or CSS modules, or even inline styles. There is not any standard for your approach to styling your elements, but in general the React community favours CSS in JS solutions, probably due to the fact that React is a framework driven heavily by JavaScript, and there are obvious benefits for the CSS in JS approach over traditional .css files.

If developers stick to plain CSS / SCSS files, it would be rather difficult to decide where to keep the styles (a dedicated CSS folder or a .css in every component folder?), how to scope the styles to make sure there are no class name clashes (should you wrap all the styles in a .css with a component-specific class? How should you override the styles later on when you use the components in other parts?), how to only import styles that are required for that particular page on the single page app, etc….

Within the CSS in JS community, styled-components is an awesome library that takes in SCSS-like CSS syntax and makes your styles component-specific.

Getting Started

Since this is an article about building a component library for a React app, the prerequisites are: basic knowledge of ES6, React, JSX, Webpack, and npm/yarn.

To begin with, in a typical React project you would be using npm/yarn, so you can install styled-components with this command:

npm install --save styled-components
OR
yarn add --dev styled-components

I would organize the folder structure as follows:

.
├── ...
├── components # components files
│ ├── TextField # TextField component
│ ├── styles.js # styled-component styles
│ ├── index.js # React class
│ └── test.js # unit tests for component code
│ ├── Dropdown # Dropdown component
│ ├── styles.js # styled-component styles
│ ├── index.js # React class
│ └── test.js # unit tests for component code
│ └── AnotherComponent # similar structure as above
├── src # components files
│ ├── Form # form page
│ ├── styles.js # styles specific to the page
│ ├── index.js # React class
│ └── test.js # unit tests for the page
├── css # folder for .css files
│ └── main.css # global CSS
├── app.js # the root that mounts the app
├── index.html # the HTML page for the SPA
└── ...

The files with bold names are the ones we will cover in this article.

./src/app.js

This is the simplest setup for the entry JavaScript file with React, JSX, and Webpack.

import React from "react";
import ReactDOM from "react-dom";
import { Form } from "./src/Form";
ReactDOM.render(
<Form />,
document.getElementById("root")
);

./src/main.css

For the blue background and centering the elements as shown on my CodePen demo, this is the global CSS we are including.

* {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
background-color: #b4edff;
font-family: Lucida Grande, Calibri;
padding: 20px;
margin: 0;
}

How to create styled components

Before we start working on the styled-components, let’s do a brief introduction on how to use it.

We can pass several kinds of arguments to styled() to instantiate the styled components. It could be a tag name like styled('p'), or we can use the alias styled.p, or pass a styled-component like I did in styled(Text), or we can pass in a function which renders a React component in (I will show that later on).

There is only one thing to beware of: we cannot directly pass a React class component into the function. We have to pass in a function that renders the React class component instead.

The back ticks part where you put the CSS in are called tagged template literals. It’s new to ES6 and you can read up on it here.

./components/TextField/styles.js

For all styles specific to this component, we would put it in the styles file under its component folder.

import styled from 'styled-components';const Text = styled.p`
margin: 8px auto;
`
const Error = styled(Text)`
font-size: 12px;
color: red;
`
const StyledInput = styled.input`
width: 100%;
font-size: 14px;
padding: 6px 8px;
border-width: 1px;
border-style: solid;
border-color: ${props => props.error ? 'red' : 'black'};
margin: 0;
`
export {
Text,
Error,
StyledInput
};

./components/TextField/index.js

On the actual component file, we import the styles defined for this component, and then we define the React component in this file. There are two ways to declare the React component:
1) use class React extends React.Component {} to create a typical React component, OR
2) use styled-components to create a styled React component

In our case for the TextField, we can use either way. But since our component will be a controlled input field, this component will not be stateful, and so it is unnecessary to define it as a React class component.

With the 2) method, remember that we need to put the back ticks even though we don’t have any styles for the TextField div or its children. My usual practice is to put all styles specific to this component in the styles file, but of course you can put all those styles in this pair of back ticks and avoid declaring so many styled components. This is just a matter of personal preference.

import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { Text, Error, StyledInput } from './styles';
const TextField = styled(({
id,
name,
value,
placeholder,
hasError,
isTouched,
hintText,
type,
onChange,
disabled,
className
}) => {
const error = isTouched && hasError;
return (
<div className={className}>
<StyledInput
id={id}
name={name}
type={type}
placeholder={placeholder}
value={value}
onChange={onChange}
disabled={disabled}
error={error}
/>
{hintText && <Text>{hintText}</Text>}
{error && <Error>{error}</Error>}
</div>
)
})``
TextField.propTypes = {
id: PropTypes.string,
value: PropTypes.string,
placeholder: PropTypes.string,
error: PropTypes.string,
hintText: PropTypes.string,
type: PropTypes.string,
onChange: PropTypes.func,
disabled: PropTypes.bool,
className: PropTypes.string
};
export {
TextField
};

That’s all for our TextField component. Ideally, you would want to write some unit tests for your components. To assert against the styles, please check out jest-styled-components. In my typical unit test files, I would include Enzyme, Jest, and jest-styled-components.

./src/Form/styles.js

These are the styles specific to the Form page.

import styled from 'styled-components';import { TextField } from '../../components/TextField/';const StyledTextField = styled(TextField)`
border-width: 2px;
border-style: dashed;
border-color: #1166ff;
box-shadow: 0 4px 4px #1166ff;
outline: none;
`
const FormRow = styled.div`
width: 500px;
margin: 20px auto;
`
export {
StyledTextField,
FormRow
};

./src/Form/index.js

Here we need a stateful component in the app to manage / control the values of the TextFields. The TextFields field1 and field2 are controlled components, and field3 is an uncontrolled component. In general, we would like all our React form components to be controlled.

The advantage of having a controlled component over an uncontrolled component is that we can programmatically change the value of the component with setState(). It would be very messy for our app if we change the value through pure JavaScript without tracking the changes with the state. If we do not track the changes with the state, why uses React in the first place? You can play around with the demo code to understand more about a controlled form component and an uncontrolled form component.

import React from 'react';import { Text } from '../../components/TextField/styles';
import { TextField } from '../../components/TextField/';
import { StyledTextField, FormRow } from './styles';
class Form extends React.Component {
state = {
values: {
field1: "Yeah I am a reusable component!",
field2: ""
}
}
onChange = event => {
const newValuesObj = Object.assign({}, this.state.values, {[event.target.name]: event.target.value});
this.setState({
values: newValuesObj
});
}
render() {
return (
<React.Fragment>
<FormRow>
<TextField
name="field1"
type="text"
value={this.state.values.field1}
placeholder="Sexy Controlled Text Field"
onChange={this.onChange}
/>
<Text>
<span>New Value: </span>
<span>{this.state.values.field1}</span>
</Text>
</FormRow>
<FormRow>
<TextField
name="field2"
type="text"
value={this.state.values.field2}
placeholder="I have an error!"
hasError="This is a required field!"
onChange={this.onChange}
isTouched
/>
</FormRow>
<FormRow>
<StyledTextField
name="field3"
type="text"
placeholder="I know I am special yeahhhh baby"
/>
</FormRow>
</React.Fragment>
)
}
}
export {
Form
};

Here’s everything combined on a CodePen demo:

Play around with my CodePen or create your own in a properly set-up React project!

You can also clone my GitHub repository for this demo:
https://github.com/fionnachan/TextField-styled-component/

Access the compiled demo on:
https://fionnachan.github.io/TextField-styled-component/

Comment below to share your thoughts with me 😉

Follow me if you like this article! I would share my front-end experiments whenever I have time 😎

--

--

Fionna Chan
Fionna Chan

Written by Fionna Chan

Frontend Unicorn. I 💖 CSS & JavaScript. My brain occasionally runs out of memory so I need to pen down my thoughts. ✨💻🦄🌈.y.at

Responses (2)