Tutorial

How To Create Custom Components in React

Published on April 16, 2020
English
How To Create Custom Components in React

The author selected Creative Commons to receive a donation as part of the Write for DOnations program.

Introduction

In this tutorial, you’ll learn to create custom components in React. Components are independent pieces of functionality that you can reuse in your application, and are the building blocks of all React applications. Often, they can be simple JavaScript functions and classes, but you use them as if they were customized HTML elements. Buttons, menus, and any other front-end page content can all be created as components. Components can also contain state information and display markdown.

After learning how to create components in React, you’ll be able to split complex applications into small pieces that are easier to build and maintain.

In this tutorial, you’ll create a list of emojis that will display their names on click. The emojis will be built using a custom component and will be called from inside another custom component. By the end of this tutorial, you’ll have made custom components using both JavaScript classes and JavaScript functions, and you’ll understand how to separate existing code into reusable pieces and how to store the components in a readable file structure.

Prerequisites

Step 1 — Setting Up the React Project

In this step, you’ll create a base for your project using Create React App. You will also modify the default project to create your base project by mapping over a list of emojis and adding a small amount of styling.

First, create a new project. Open a terminal, then run the following command:

  1. npx create-react-app tutorial-03-component

Once this is finished, change into the project directory:

  1. cd tutorial-03-component

Open the App.js code in a text editor:

  1. nano src/App.js

Next, take out the template code created by Create React App, then replace the contents with new React code that displays a list of emojis:

tutorial-03-component/src/App.js
import React from 'react';
import './App.css';

const displayEmojiName = event => alert(event.target.id);
const emojis = [
  {
    emoji: '😀',
    name: "test grinning face"
  },
  {
    emoji: '🎉',
    name: "party popper"
  },
  {
    emoji: '💃',
    name: "woman dancing"
  }
];

function App() {
  const greeting = "greeting";
  const displayAction = false;
  return(
    <div className="container">
      <h1 id={greeting}>Hello, World</h1>
      {displayAction && <p>I am writing JSX</p>}
      <ul>
        {
          emojis.map(emoji => (
            <li key={emoji.name}>
              <button
                onClick={displayEmojiName}
              >
                <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
              </button>
            </li>
          ))
        }
      </ul>
    </div>
  )
}

export default App;

This code uses JSX syntax to map() over the emojis array and list them as <li> list items. It also attaches onClick events to display emoji data in the browser. To explore the code in more detail, check out How to Create React Elements with JSX, which contains a detailed explanation of the JSX.

Save and close the file. You can now delete the logo.svg file, since it was part of the template and you are not referencing it anymore:

  1. rm src/logo.svg

Now, update the styling. Open src/App.css:

  1. nano src/App.css

Replace the contents with the following CSS to center the elements and adjust the font:

tutorial-03-component/src/App.css
.container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

button {
    font-size: 2em;
    border: 0;
    padding: 0;
    background: none;
    cursor: pointer;
}

ul {
    display: flex;
    padding: 0;
}

li {
    margin: 0 20px;
    list-style: none;
    padding: 0;
}

This uses flex to center the main <h1> and list elements. It also removes default button styles and <li> styles so the emojis line up in a row. More details can be found at How to Create React Elements with JSX.

Save and exit the file.

Open another terminal window in the root of your project. Start the project with the following command:

  1. npm start

After the command runs, you’ll see the project running in your web browser at http://localhost:3000.

Leave this running the entire time you work on your project. Every time you save the project, the browser will auto-refresh and show the most up-to-date code.

You will see your project page with Hello, World and the three emojis that you listed in your App.js file:

Browser with emoji

Now that you’ve set up your code, you can now start putting together components in React.

Step 2 — Creating an Independent Component with React Classes

Now that you have your project running, you can start making your custom component. In this step, you’ll create an independent React component by extending the base React Component class. You’ll create a new class, add methods, and use the render function to show data.

React components are self-contained elements that you can reuse throughout a page. By making small, focused pieces of code, you can move and reuse pieces as your application grows. The key here is that they are self-contained and focused, allowing you to separate out code into logical pieces. In fact, you have already been working with logically separated components: The App.js file is a functional component, one that you will see more of in Step 3.

There are two types of custom component: class-based and functional. The first component you are going to make is a class-based component. You will make a new component called Instructions that explains the instructions for the emoji viewer.

Note: Class-based components used to be the most popular way of creating React components. But with the introduction of React Hooks, many developers and libraries are shifting to using functional components.

Though functional components are now the norm, you will often find class components in legacy code. You don’t need to use them, but you do need to know how to recognize them. They also give a clear introduction to many future concepts, such as state management. In this tutorial, you’ll learn to make both class and functional components.

To start, create a new file. By convention, component files are capitalized:

  1. touch src/Instructions.js

Then open the file in your text editor:

  1. nano src/Instructions.js

First, import React and the Component class and export Instructions with the following lines:

tutorial-03-component/src/Instructions.js
import React, { Component } from 'react';

export default class Instructions extends Component {}

Importing React will convert the JSX. Component is a base class that you’ll extend to create your component. To extend that, you created a class that has the name of your component (Instructions) and extended the base Component with the export line. You’re also exporting this class as the default with export default keywords at the start of the class declaration.

The class name should be capitalized and should match the name of the file. This is important when using debugging tools, which will display the name of the component. If the name matches the file structure, it will be easier to locate the relevant component.

The base Component class has several methods you can use in your custom class. The most important method, and the only one you’ll use in this tutorial, is the render() method. The render() method returns the JSX code that you want to display in the browser.

To start, add a little explanation of the app in a <p> tag:

tutorial-03-component/src/Instructions.js
import React, { Component } from 'react';

export class Instructions extends Component {

  render() {
    return(
      <p>Click on an emoji to view the emoji short name.</p>
    )
  }

}

Save and close the file. At this point, there’s still no change to your browser. That’s because you haven’t used the new component yet. To use the component, you’ll have to add it into another component that connects to the root component. In this project, <App> is the root component in index.js. To make it appear in your application, you’ll need to add to the <App> component.

Open src/App.js in a text editor:

  1. nano src/App.js

First, you’ll need to import the component:

tutorial-03-component/src/App.js
import React from 'react';

import Instructions from './Instructions';

import './App.css';

...

export default App;

Since it’s the default import, you could import to any name you wanted. It’s best to keep the names consistent for readability—the import should match the component name, which should match the file name—but the only firm rule is that the component must start with a capital letter. That’s how React knows it’s a React component.

Now that you’ve imported the component, add it to the rest of your code as if it were a custom HTML element:

tutorial-03-component/src/App.js
import React from 'react';

import Instructions from './Instructions.js'

...
function App() {
  const greeting = "greeting";
  const displayAction = false;
  return(
    <div className="container">
      <h1 id={greeting}>Hello, World</h1>
      {displayAction && <p>I am writing JSX</p>}
      <Instructions />
      <ul>
        {
          emojis.map(emoji => (
            <li key={emoji.name}>
              <button
                onClick={displayEmojiName}
              >
                <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
              </button>
            </li>
          ))
        }
      </ul>
    </div>
  )
}

export default App;

In this code, you wrapped the component with angle brackets. Since this component doesn’t have any children, it can be self closing by ending with />.

Save the file. When you do, the page will refresh and you’ll see the new component.

Browser with instruction text

Now that you have some text, you can add an image. Download an emoji image from wikimedia and save it in the src directory as emoji.svg with the following command:

  1. curl -o src/emoji.svg https://upload.wikimedia.org/wikipedia/commons/3/33/Twemoji_1f602.svg

curl makes the request to the URL, and the -o flag allows you to save the file as src/emoji.svg.

Next, open your component file:

  1. nano src/Instructions.js

Import the emoji and add it to your custom component with a dynamic link:

tutorial-03-component/src/Instructions.js
import React, { Component } from 'react';
import emoji from './emoji.svg'

export default class Instructions extends Component {

  render() {
    return(
      <>
        <img alt="laughing crying emoji" src={emoji} />
        <p>Click on an emoji to view the emoji short name.</p>
      </>
    )
  }
}

Notice that you need to include the file extension .svg when importing. When you import, you are importing a dynamic path that is created by webpack when the code compiles. For more information, refer to How To Set Up a React Project with Create React App.

You also need to wrap the <img> and <p> tags with empty tags to ensure that you are returning a single element.

Save the file. When you reload, the image will be very large compared to the rest of the content:

Browser window with large emoji image

To make the image smaller, you’ll need to add some CSS and a className to your custom component.

First, in Instructions.js, change the empty tags to a div and give it a className of instructions:

tutorial-03-component/src/Instructions.js
import React, { Component } from 'react';
import emoji from './emoji.svg'

export default class Instructions extends Component {

  render() {
    return(
      <div className="instructions">
        <img alt="laughing crying emoji" src={emoji} />
        <p>Click on an emoji to view the emoji short name.</p>
      </div>
    )
  }
}

Save and close the file. Next open App.css:

  1. nano src/App.css

Create rules for the .instructions class selector:

tutorial-03-component/src/App.css
.container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

...

.instructions {
    display: flex;
    flex-direction: column;
    align-items: center;
}

When you add a display of flex styling, you make the img and the p centered with flexbox. You changed the direction so that everything lines up vertically with flex-direction: column;. The line align-items: center; will center the elements on the screen.

Now that your elements are lined up, you need to change the image size. Give the img inside the div a width and height of 100px.

tutorial-03-component/src/App.css
.container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

...

.instructions {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.instructions img {
    width: 100px;
    height: 100px;
}

Save and close the file. The browser will reload and you’ll see the image is much smaller:

Browser window with smaller image

At this point, you’ve created an independent and reusable custom component. To see how it’s reusable, add a second instance to App.js.

Open App.js:

  1. nano src/App.js

In App.js, add a second instance of the component:

tutorial-03-component/src/App.js
import React from 'react';

import Instructions from './Instructions.js'

...

function App() {
  const greeting = "greeting";
  const displayAction = false;
  return(
    <div className="container">
      <h1 id={greeting}>Hello, World</h1>
      {displayAction && <p>I am writing JSX</p>}
      <Instructions />
      <Instructions />
      <ul>
        {
          emojis.map(emoji => (
            <li key={emoji.name}>
              <button
                onClick={displayEmojiName}
              >
                <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
              </button>
            </li>
          ))
        }
      </ul>
    </div>
  )
}

export default App;

Save the file. When the browser reloads, you’ll see the component twice.

Browser with two instances of the Instructions component

In this case, you wouldn’t want two instances of Instructions, but you can see that the component can be efficiently reused. When you create custom buttons or tables, you will likely use them multiple times on one page, making them perfect for custom components.

For now, you can delete the extra image tag. In your text editor, delete the second <Instructions /> and save the file:

tutorial-03-component/src/App.js
import React from 'react';

import Instructions from './Instructions.js'

...

function App() {
  const greeting = "greeting";
  const displayAction = false;
  return(
    <div className="container">
      <h1 id={greeting}>Hello, World</h1>
      {displayAction && <p>I am writing JSX</p>}
      <Instructions />
      <ul>
        {
          emojis.map(emoji => (
            <li key={emoji.name}>
              <button
                onClick={displayEmojiName}
              >
                <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
              </button>
            </li>
          ))
        }
      </ul>
    </div>
  )
}

export default App;

Now you have a reusable, independent component that you can add to a parent component multiple times. The structure you have now works for a small number of components, but there is a slight problem. All of the files are mixed together. The image for <Instructions> is in the same directory as the assets for <App>. You also are mixing the CSS code for <App> with the CSS for <Instructions>.

In the next step, you’ll create a file structure that will give each component independence by grouping their functionality, styles, and dependencies together, giving you the ability to move them around as you need.

Step 3 — Creating a Readable File Structure

In this step, you’ll create a file structure to organize your components and their assets, such as images, CSS, and other JavaScript files. You’ll be grouping code by component, not by asset type. In other words, you won’t have a separate directory for CSS, images, and JavaScript. Instead you’ll have a separate directory for each component that will contain the relevant CSS, JavaScript, and images. In both cases, you are separating concerns.

Since you have an independent component, you need a file structure that groups the relevant code. Currently, everything is in the same directory. List out the items in your src directory:

  1. ls src/

The output will show that things are getting pretty cluttered:

Output
App.css Instructions.js index.js App.js emoji.svg serviceWorker.js App.test.js index.css setupTests.js

You have code for the <App> component (App.css, App.js, and App.test.js) sitting alongside your root component (index.css and index.js) and your custom component Instructions.js.

React is intentionally agnostic about file structure. It does not recommend a particular structure, and the project can work with a variety of different file hierarchies. But we recommend to add some order to avoid overloading your root directory with components, CSS files, and images that will be difficult to navigate. Also, explicit naming can make it easier to see which pieces of your project are related. For example, an image file named Logo.svg may not clearly be part of a component called Header.js.

One of the simplest structures is to create a components directory with a separate directory for each component. This will allow you to group your components separately from your configuration code, such as serviceWorker, while grouping the assets with the components.

Creating a Components Directory

To start, create a directory called components:

  1. mkdir src/components

Next, move the following components and code into the directory: App.css, App.js, App.test.js, Instructions.js, and emoji.svg:

  1. mv src/App.* src/components/
  2. mv src/Instructions.js src/components/
  3. mv src/emoji.svg src/components/

Here, you are using a wildcard (*) to select all files that start with App..

After you move the code, you’ll see an error in your terminal running npm start.

Output
Failed to compile. ./src/App.js Error: ENOENT: no such file or directory, open 'your_file_path/tutorial-03-component/src/App.js'

Remember, all of the code is importing using relative paths. If you change the path for some files, you’ll need to update the code.

To do that, open index.js.

  1. nano src/index.js

Then change the path of the App import to import from the components/ directory.

tutorial-03-component/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import * as serviceWorker from './serviceWorker';

...

serviceWorker.unregister();

Save and close the file. Your script will detect the changes and the error will disappear.

Now you have components in a separate directory. As your applications become more complex, you may have directories for API services, data stores, and utility functions. Separating component code is the first step, but you still have CSS code for Instructions mixed in the App.css file. To create this logical separation, you will first move the components into separate directories.

Moving Components to Individual Directories

First, make a directory specifically for the <App> component:

  1. mkdir src/components/App

Then move the related files into the new directory:

  1. mv src/components/App.* src/components/App

When you do you’ll get a similar error to the last section:

Output
Failed to compile. ./src/components/App.js Error: ENOENT: no such file or directory, open 'your_file_path/tutorial-03-component/src/components/App.js'

In this case, you’ll need to update two things. First, you’ll need to update the path in index.js.

Open the index.js file:

  1. nano src/index.js

Then update the import path for App to point to the App component in the App directory.

tutorial-03-component/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './serviceWorker';

...

serviceWorker.unregister();

Save and close the file. The application still won’t run. You’ll see an error like this:

Output
Failed to compile. ./src/components/App/App.js Module not found: Can't resolve './Instructions.js' in 'your_file_path/tutorial-03-component/src/components/App'

Since <Instructions> is not on the same directory level as the <App> component, you’ll need to change the import path. Before that, create a directory for Instructions. Make a directory called Instructions in the src/components directory:

  1. mkdir src/components/Instructions

Then move Instructions.js and emoji.svg into that directory:

  1. mv src/components/Instructions.js src/components/Instructions
  2. mv src/components/emoji.svg src/components/Instructions

Now that the Instructions component directory has been created, you can finish updating the file paths to connect your component to your app.

Updating import Paths

Now that components are in individual directories, you can adjust the import path in App.js.

Open App.js:

  1. nano src/components/App/App.js

Since the path is relative, you’ll need to move up one directory—src/components—then into the Instructions directory for Instructions.js, but since this is a JavaScript file, you don’t need the final import.

tutorial-03-component/src/components/App/App.js
import React from 'react';

import Instructions from '../Instructions/Instructions.js';

import './App.css';

...

export default App;

Save and close the file. Now that your imports are all using the correct path, you’re browser will update and show the application.

Browser window with smaller image

Note: You can also call the root file in each directory index.js. For example, instead of src/components/App/App.js you could create src/components/App/index.js. The advantage to this is that your imports are slightly smaller. If the path points to a directory, the import will look for an index.js file. The import for src/components/App/index.js in the src/index.js file would be import ./components/App. The disadvantage of this approach is that you have a lot of files with the same name, which can make it difficult to read in some text editors. Ultimately, it’s a personal and team decision, but it’s best to be consistent.

Separating Code in Shared Files

Now each component has its own directory, but not everything is fully independent. The last step is to extract the CSS for Instructions to a separate file.

First, create a CSS file in src/components/Instructions:

  1. touch src/components/Instructions/Instructions.css

Next, open the CSS file in your text editor:

  1. nano src/components/Instructions/Instructions.css

Add in the instructions CSS that you created in an earlier section:

tutorial-03-component/src/components/Instructions/Instructions.css
.instructions {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.instructions img {
    width: 100px;
    height: 100px;
}

Save and close the file. Next, remove the instructions CSS from src/components/App/App.css.

  1. nano src/components/App/App.css

Remove the lines about .instructions. The final file will look like this:

tutorial-03-component/src/components/App/App.css
.container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

button {
    font-size: 2em;
    border: 0;
    padding: 0;
    background: none;
    cursor: pointer;
}

ul {
    display: flex;
    padding: 0;
}

li {
    margin: 0 20px;
    list-style: none;
    padding: 0;
}

Save and close the file. Finally, import the CSS in Instructions.js:

  1. nano src/components/Instructions/Instructions.js

Import the CSS using the relative path:

tutorial-03-component/src/components/Instructions/Instructions.js
import React, { Component } from 'react';
import './Instructions.css';
import emoji from './emoji.svg'

export default class Instructions extends Component {

  render() {
    return(
      <div className="instructions">
        <img alt="laughing crying emoji" src={emoji} />
        <p>Click on an emoji to view the emoji short name.</p>
      </div>
    )
  }
}

Save and close the file. Your browser window will look as it did before, except now all the file assets are grouped in the same directory.

Browser window with smaller image

Now, take a final look at the structure. First, the src/ directory:

  1. ls src

You have the root component index.js and the related CSS index.css next to the components/ directory and utility files such as serviceWorker.js and setupTests.js:

Output
components serviceWorker.js index.css setupTests.js index.js

Next, look inside components:

  1. ls src/components

You’ll see a directory for each component:

Output
App Instructions

If you look inside each component, you’ll see the component code, CSS, test, and image files if they exist.

  1. ls src/components/App
Output
App.css App.js App.test.js
  1. ls src/components/Instructions
Output
Instructions.css Instructions.js emoji.svg

At this point, you’ve created a solid structure for your project. You moved a lot of code around, but now that you have a structure, it will scale easier.

This is not the only way to compose your structure. Some file structures can take advantage of code splitting by specifying a directory that will be split into different packages. Other file structures split by route and use a common directory for components that are used across routes.

For now, stick with a less complex approach. As a need for another structure emerges, it’s always easier to move from simple to complex. Starting with a complex structure before you need it will make refactoring difficult.

Now that you have created and organized a class-based component, in the next step you’ll create a functional component.

Step 4 — Building a Functional Component

In this step, you’ll create a functional component. Functional components are the most common component in contemporary React code. These components tend to be shorter, and unlike class-based components, they can use React hooks, a new form of state and event management.

A functional component is a JavaScript function that returns some JSX. It doesn’t need to extend anything and there are no special methods to memorize.

To refactor <Instructions> as a functional component, you need to change the class to a function and remove the render method so that you are left with only the return statement.

To do that, first open Instructions.js in a text editor.

  1. nano src/components/Instructions/Instructions.js

Change the class declaration to a function declaration:

tutorial-03-component/src/components/Instructions/Instructions.js
import React, { Component } from 'react';
import './Instructions.css';
import emoji from './emoji.svg'

export default function Instructions() {
  render() {
    return(
      <div className="instructions">
        <img alt="laughing crying emoji" src={emoji} />
        <p>Click on an emoji to view the emoji short name.</p>
      </div>
    )
  }
}

Next, remove the import of { Component }:

tutorial-03-component/src/components/Instructions/Instructions.js
import React from 'react';
import './Instructions.css';
import emoji from './emoji.svg'

export default function Instructions() {

  render() {
    return(
      <div className="instructions">
        <img alt="laughing crying emoji" src={emoji} />
        <p>Click on an emoji to view the emoji short name.</p>
      </div>
    )
  }
}

Finally, remove the render() method. At that point, you are only returning JSX.

tutorial-03-component/src/components/Instructions/Instructions.js
import React from 'react';
import './Instructions.css';
import emoji from './emoji.svg'

export default function Instructions() {
  return(
    <div className="instructions">
        <img alt="laughing crying emoji" src={emoji} />
        <p>Click on an emoji to view the emoji short name.</p>
    </div>
  )
}

Save the file. The browser will refresh and you’ll see your page as it was before.

Browser with emoji

You could also rewrite the function as an arrow function using the implicit return. The main difference is that you lose the function body. You will also need to first assign the function to a variable and then export the variable:

tutorial-03-component/src/components/Instructions/Instructions.js
import React from 'react';
import './Instructions.css';
import emoji from './emoji.svg'

const Instructions = () => (
  <div className="instructions">
    <img alt="laughing crying emoji" src={emoji} />
    <p>Click on an emoji to view the emoji short name.</p>
  </div>
)

export default Instructions;

Simple functional components and class-based components are very similar. When you have a simple component that doesn’t store state, it’s best to use a functional component. The real difference between the two is how you store a component’s state and use properties. Class-based components use methods and properties to set state and tend to be a little longer. Functional components use hooks to store state or manage changes and tend to be a little shorter.

Conclusion

Now you have a small application with independent pieces. You created two major types of components: functional and class. You separated out parts of the components into directories so that you could keep similar pieces of code grouped together. You also imported and reused the components.

With an understanding of components, you can start to look at your applications as pieces that you can take apart and put back together. Projects become modular and interchangable. The ability to see whole applications as a series of components is an important step in thinking in React. If you would like to look at more React tutorials, take a look at our React Topic page, or return to the How To Code in React.js series page.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


Tutorial Series: How To Code in React.js

React is a popular JavaScript framework for creating front-end applications, such as user interfaces that allow users to interact with programs. Originally created by Facebook, it has gained popularity by allowing developers to create fast applications using an intuitive programming paradigm that ties JavaScript with an HTML-like syntax known as JSX.

In this series, you will build out examples of React projects to gain an understanding of this framework, giving you the knowledge you need to pursue front-end web development or start out on your way to full stack development.

About the authors

Default avatar

Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel