Skip to main content

Creating components

The philosophy behind modern architecture in front-end development is based around using components. These help us create reusable and comprehensible parts that together make up an application.

React comes with a helpful JavaScript library extension named JSX. This provides us a developer-friendly way of writing HTML elements in JavaScript the same way we would write regular HTML.

JSX makes React components more readable and maintainable while providing the full power of JavaScript within your markup. It's an essential part of the React ecosystem that significantly improves the developer experience when building user interfaces. Check out the further readings for more about JSX.

Header.jsx
export const Header = () => {
return (
<header>
<nav>
<ol>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ol>
</nav>
</header>
);
}

The function is written in PascalCase to indicate that it's a React component. In order to render our Header component in other files, we need to export it by prefixing the function with export like this: export const Header = () => {.

Working with components

The Header component we've created can be rendered on our homepage by importing it. In order to do so we must store it somewhere first and export it from there.

It's conventional to have a directory named components/ in the directory src/.

Another is to create a directory, written in kebab-case, for each component. In there we store the component itself, its styling, tests and an index.js for convenience purposes. This way our source code won't get cluttered; imagine all these files together in the components/ directory with 50 different components.

For now we'll just have the Header.jsx and an index.js in the directory header/. So, at this point the directory structure will (mostly) look like this:

task-flow/
└─ src/
└─ app/
├- layout.jsx
└- page.jsx
└─ components/
├- header/
| ├- Header.jsx
| └- index.js
└─ ... your other components

All the index.js file needs to contain is export { Header } from './Header';. Because of this we can now import the header component as follows:

import { Header } from '../header/';

Instead of:

import { Header } from '../header/Header';

Especially for components with longer names this will be very useful.

Next.js Layout

Once the component has been imported it can be used in the JSX template.

In this specific use-case, the header can be part of the Next.js Layout structure.

src/app/layout.jsx
import type { Metadata } from "next";
import { Header } from '../src/components/header/';

export const metadata: Metadata = {
title: "TaskFlow",
description: "Personal time entries",
};

export default function RootLayout() {
return (
<html lang="en">
<body>
<Header />
{children}
</body>
</html>
);
};

Named exports vs. default exports

Functions and variables prefixed with export are called "named exports", which require you to import them as:

export const Header from './Header';
import { variableName } from './some-file';

Alternatively, you can use export default variableName;. Default exports can be imported with the syntax:

import variableName from './some-file';

Each file can only have one default export.

Another difference between these two approaches is that named exports are "name-sensitive", meaning that you must use the exact same name to both export and import the variable or function. If these don't match, an error is thrown.

That's why it's usually preferable to use named exports; it might prevent you and others from wrongfully assuming what's the default export.

info

In Next.js they create the URLS out of the app pages via the default export. This means for this specific use case default exports are needed to make your app work.

Index files

A widely used convention is to automatically pick up on index files when importing files. Node follows this convention and lets you shorten your imports by having an index.js file. We import and export the component in one single line. Watch this:

export { Header } from './Header'

Fragments

All JSX templates must have exactly one root element or component. In the previous example of the TimeEntries component we returned a single TimeEntry component, but in real life there will most likely be several. Abstractly put, it would result in something like this:

TimeEntries.jsx
import { TimeEntry } from '../time-entry/';

export const TimeEntries = () => {
return (
<TimeEntry />
<TimeEntry />
<TimeEntry />
);
}

This doesn't meet React's requirement of having one single HTML element or React component as root. Having an HTML element as root element would look like this:

TimeEntries.jsx
import { TimeEntry } from '../time-entry/';

export const TimeEntries = () => {
return (
<div>
<TimeEntry />
<TimeEntry />
<TimeEntry />
</div>
);
}

Instead of the <div> HTML element that was used as root in this example, a React component might be used as root as well.

Sometimes you won't be needing a root element or component though. For that use-case React has introduced so called "fragments". This is a component included in the React library that can be used as root component.

TimeEntries.jsx
import { TimeEntry } from '../time-entry/';

export const TimeEntries = () => {
return (
<React.Fragment>
<TimeEntry />
<TimeEntry />
<TimeEntry />
</React.Fragment>
);
}

The <React.Fragment /> won't render any HTML or styles. It's just there to wrap your elements and components.

There's also a short-hand notation available for fragments. You can simply omit the React.Fragment part so it won't be distracting:

TimeEntries.jsx
import { TimeEntry } from '../time-entry/';

export const TimeEntries = () => {
return (
<>
<TimeEntry />
<TimeEntry />
<TimeEntry />
</>
);
}

Further reading