React is one of the most popular JavaScript libraries for building user interfaces. To build an application using React, you just need to build encapsulated components that can contain their own state. Your application should basically be a collection of composable React components which can be reused multiple times.

What are reusable components?

Reusable components are those React components that can be used multiple times in your application. As a result, they need to be generic enough so that it’s free from complex business logic. If a component contains any complex logic inside it, not only does it become difficult to reuse, it also becomes less maintainable. React Hooks are the perfect fit for reusable component logic.

For example, the App component below has a button which can’t be reused since it has the onClick prop hardcoded in it:

function handleClick(e) { // Some function which does fake API call fakeApiCall(e.target.value); } function App() { return ( <div className="app"> <button className="button" onClick={handleClick}> Submit </button> </div> ); }

The above code renders the following user interface:

Here, there is no way in which we can modify the text Submit which is rendered on the button.

Making a React component reusable

If we want to make the above component reusable, we need to make it more generic. First, we can make a separate Button function which can be imported and reused multiple times in our application:

function handleClick(e) { // Some function which does fake API call fakeApiCall(e.target.value); } function Button() { return ( <button className="button" onClick={handleClick}> Submit </button> ); }



Then, we can reuse that Button component multiple times inside our App function:

function App() { return ( <div className="app"> <Button /> <Button /> </div> ); }



The above code renders the following user interface:





As you can see, we are already reusing one component multiple times. But, we still need to make it more generic because we might want to do different tasks with one click of a button. We can do a form submit, form reset or do another API call to fetch some data, for example.

Let’s extract the handleClick function from our Button component and pass it as a prop. Our Button component will now look like the following:

function Button(props) { return ( <button className="button" onClick={props.handleClick}> Submit </button> ); }

And our App component will look like the following:

function handleClick(e) { // Some function which does fake API call fakeApiCall(e.target.value); } function App() { return ( <div className="app"> <Button handleClick={handleClick} /> </div> ); }

As you can see, we can pass any function to the Button component through the handleClick prop. I highly suggest that you check your props using PropTypes.

We can also use multiple Button components inside our App component:

function handleAPICall(e) { // Do some API call } function handleFormReset(e) { // Reset some form data } function App() { return ( <div className="app"> <Button handleClick={handleAPICall} /> <Button handleClick={handleFormReset} /> </div> ); }



As you can see, we have made our Button component even more flexible. We can also pass the text which is rendered on the button as a prop.

Our Button component will now look like the following:

function Button(props) { return ( <button className="button" onClick={props.handleClick}> {props.label} </button> ); }

And our App component will look like the following:

function handleAPICall(e) { // Do some API call } function handleFormReset(e) { // Reset some form data } function App() { return ( <div className="app"> <Button handleClick={handleAPICall} label="Submit"/> <Button handleClick={handleFormReset} label="Reset"/> </div> ); }

It renders the following user interface:







It’s already very reusable. But, we can also add certain additional props like whether to render an icon before the label of the button as well.

To do that, we can change our Button component to the following:

function Button(props) { return ( <button className="button" onClick={props.handleClick}> {props.icon} {props.label} </button> ); }



And, we need to pass that icon prop from our App component:





<Button handleClick={handleAPICall} label="Submit" icon={<i className="fas fa-arrow-alt-circle-right" />} />



The above example uses font-awesome but you can use any font you want. The above changes render the following user interface:







Also, it’s a good idea to render the icon prop only if it’s present. To do that, we just need to make the following changes to our Button component:

function Button(props) { return ( <button className="button" onClick={props.handleClick}> {props.icon && props.icon} {props.label} </button> ); }

Our component is very much reusable at this moment. We can also pass another additional prop called type which can control whether the button will be primary or secondary.

We need to make the following changes to our Button component:

function Button(props) { const className = `button ${props.type}` return ( <button className={className} onClick={props.handleClick}> {props.icon && props.icon} {props.label} </button> ); }



Here, we will be passing a type prop from our App component which will be passed to the className of the button.

Our App component will now look like the following:

function handleAPICall(e) { // Do some API call } function handleFormReset(e) { // Reset some form data } function App() { return ( <div className="app"> <Button handleClick={handleAPICall} label="Submit" icon={<i className="fas fa-arrow-alt-circle-right" />} type="primary" /> <Button handleClick={handleFormReset} label="Reset" type="secondary" /> </div> ); }

We would also need to add a few lines of CSS to our application:

.button.primary { background-color: #0886ff; } .button.secondary { background-color: #73a800; }

This will render the following user interface:







Finally, we can distinguish between our primary and secondary buttons. It’s also a good idea now to add a default prop to our Button component so that it renders secondary buttons by default. This is really helpful if we forget to pass the type prop to our Button component. We need to make the following changes to our Button component:

function Button(props) { const className = `button ${props.type}` return ( <button className={className} onClick={props.handleClick}> {props.icon && props.icon} {props.label} </button> ); } Button.defaultProps = { type: "secondary" };



Now, if we have another Button component which doesn’t have the type prop, it will be a secondary button:

<div className="app"> <Button handleClick={handleAPICall} label="Submit" icon={<i className="fas fa-arrow-alt-circle-right" />} type="primary" /> <Button handleClick={handleFormReset} label="Reset" type="secondary" /> <Button handleClick={handleFormReset} label="Click" /> </div>



The above code will render the following user interface:



Integrating React components with ButterCMS

Lets create a form and integrate that form with ButterCMS. First, you need to check if you have write API access. You can check that out on settings. Then, we need to create a function App which will contain a form:

function App() { const [name, updateName] = useState(""); function handleSubmit(e) { e.preventDefault(); api(name); } return ( <div className="app"> <form onSubmit={handleSubmit}> <input type="text" name="name" className="input" value={name} onChange={e => updateName(e.target.value)} /> <Button handleClick={handleSubmit} label="Submit" icon={<i className="fas fa-arrow-alt-circle-right" />} type="primary" /> </form> </div> ); }

The function App contains another function handleSubmit which will be responsible for calling a function api which will be responsible for doing an API call to save the data to ButterCMS. The api function looks like the following:

export default function api(name) { const BUTTER_CMS_API_TOKEN = "YOUR_BUTTER_CMS_WRITE_API_TOKEN"; const URL = "https://api.buttercms.com/v2/content/"; fetch(URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Token ${BUTTER_CMS_API_TOKEN}` }, body: JSON.stringify({ key: "user_details", status: "published", fields: [ { name } ] }) }).then(response => response.json()); }

Here, we will use the write API of ButterCMS to save our form data as a collection. To do that, you can go to the collections section of ButterCMS. You can add a new collection by clicking on the New Collection button. You will need to fill in the details in the form. We will save the label as User details and the API Slug will be user_details (which will be sent as key argument in our POST request):

You can click on the Save button to save your collection. Next, you can click on the Add New Property to add a new property. This is the field which you will be saving in ButterCMS.

Here, we have saved the Content Label as Name and the API Slug as name. So, the key of the field which we be saving on the form (from our client app) will be called name. You can click on Save Property to save the property.

Now, we can type on the input box of our form and click on Submit. That should save the data to ButterCMS collections.

Now, if we go to our collections, we can see that the details has been saved:

You can check out this CodeSandbox to view the implementation of this whole process. You’ll need to enter your ButterCMS Write API Token (which can be found in your settings) in the butter-cms-api-call.js file.

Conclusion

As you can now see that we have made a pretty reusable React Button component. You can use the same idea to build a component in React. The whole code is also available on CodeSandbox.

I hope this tutorial helps you in your future projects. Please feel free to share your feedback in the comments section below.

Learn to build a serverless React application in our new blog post on how to use Serverless in practice.