30 Days of NextJS — Part 2: React Fundamentals

Fredrik Erasmus
11 min readMay 21, 2024

--

Introduction

In my previous post I discovered the difference between SSR as seen in the Node world versus more conventional approaches as seen with ASP.NET.

In this post I want to do some more exploring. Specifically, around building NextJS apps. NextJS is a full stack ReactJS framework for building web applications. I have been writing ReactJS code for about 16 months now. Prior to ReactJS I used Angular in many projects for many years. I used .NET almost exclusively for project work. .NET comes with its own sets of related technologies such as ASP.NET Core. To be honest I still have a lot to learn about ReactJS. I have a lot to learn about software development — I guess that is what keeps me going.

My goal here is to cover NextJS but also ReactJS — like learn about the inner workings of NextJS apps are built. But I also have a keen desire to become a better TypeScript and JavaScript dev.

In the following sections I will cover some ReactJS basics. The content I am referring comes directly from the NextJS website. I find I learn best when I can explain something using my own words. My approach is to take my time working through content; often making my own notes. I find going through the process of doing things in a narrative style helps to keep it fresh in my mind for a while longer. We do tend to forget things fairly quickly. I have been reading through some of the learning content available on NextJS. I got to a point in going through the content with a lingering thought. I’m definitely not going to work through everything without writing some of my own code. You know when you whisk through some content, hoping some of it will stick? But if I want to have a longer-lasting and more fulfilling experience it might be better to take the time. I specifically thought about taking some of the examples shown in the learning content with my own adaptations. I think we often forget in learning about programming and code related topics to try and make it a personalized experience. And I’m not going to follow the exact steps in the section on NextJS called Updating UI with Javascript. I will, however, follow the core concepts.

The connection between NextJS and ReactJS

The NextJS learning material covers some basic ReactJS. Fundamentally speaking you cannot really use NextJS effectively without knowing ReactJS. So in the following sections I will be working through the ReactJS foundations as provided on the NextJS resource.

React is a JavaScript library for building interactive user interfaces. By library, we mean React provides helpful functions (APIs) to build UI, but leaves it up to the developer where to use those functions in their application.

What do we mean when we talk about libraries and frameworks? Sometimes developers use the term interchangeably — but for me they are different. A framework typically has a ton more features pre-built for you whereas a library such as ReactJS leaves a lot of features up to you. AngularJS provides routing out of the box whereas ReactJS does not, for instance. You can quite easily run a ReactJS app on a single server-side page without any other dependencies. You can, for instance, use an ASP.NET MVC Razor view with a script tag pointing to your ReactJS application. In such a scenario ReactJS is a library because you are effectively making use of server side routing. If, however, you do decide to use routing with ReactJS it becomes more of an SPA. With an SPA the routing is handled on the client-side by an additional ReactJS package. Routing is not included by default in a standard ReactJS app. You have to add an additional NPM package to make use of routing. Since the routing is handled client side the notion of distinct pages does not exist. You could in theory have your ReactJS running in an ASP.NET Core MVC razor view with client side routing. The notion of a “page” is really just ReactJS’s routing rendering different “views” of your app using the browser’s address bar. In many situations you have to configure your server side routing to let the client side routing take precedence otherwise you might end up with 404's.

An illustrative example can be seen in the diagram.

By library, we mean React provides helpful functions (APIs) to build UI, but leaves it up to the developer where to use those functions in their application.

One of the key features of ReactJS is its use of a virtual DOM. Going into the heavy details of how the Virtual DOM works is going to take up a lot of time, and typing. But basically the Virtual DOM is a JavaScript object that mimics the structure of the actual DOM. It is a virtual representation of the UI that React keeps in memory and syncs with the real DOM by applying minimal updates.

The Virtual DOM improves performance by minimizing the number of direct manipulations to the real DOM, which can be slow and resource intensive. Instead, ReactJS creates a lightweight copy of the actual DOM, allowing it to efficiently determine the minimal number of changes needed to keep the real DOM in sync.

Part of React’s success is that it is relatively unopinionated about the other aspects of building applications. This has resulted in a flourishing ecosystem of third-party tools and solutions, including Next.js.

It also means, however, that building a complete React application from the ground up requires some effort. Developers need to spend time configuring tools and reinventing solutions for common application requirements.

In many cases you might start out with a “simple” ReactJS app and you may not think you need additional features at first but as is typical with many software development projects you end up needing more than just “vanilla” ReactJS.

Next.js is a React framework that gives you building blocks to create web applications.

Its an interesting departure point to think of a full stack ReactJS app — it means you actually run JavaScript on the server as well, not just on the client.

By framework, we mean Next.js handles the tooling and configuration needed for React, and provides additional structure, features, and optimizations for your application.

I have seen many label NextJS as opinionated whereas ReactJS itself is less opinionated and allows you more freedom. Its not a bad thing but it is worth noting the way NextJS apps are built follows a particular way of building. Here is an attempted illustration using Markdown syntax.

You can use React to build your UI, then incrementally adopt Next.js features to solve common application requirements such as routing, data fetching, and caching — all while improving the developer and end-user experience.

Alluding to my point earlier about using ReactJS in the starting phase of your project and ultimately needing more than just the basics.

Whether you’re an individual developer or part of a larger team, you can use React and Next.js to build fully interactive, highly dynamic, and performant web applications.

I guess we will see!

When you visit a page using your browser

When you open a webpage, the content you view in the browser is rendered, but you don’t see the mechanics behind it. The server you connect to using the URL you type into the address bar returns markup (HTML). Your browser uses the DOM (Document Object Model) to translate the HTML sent by the server. The DOM consists of a tree-like structure where elements in the HTML are treated as objects.

ReactJS is a JavaScript library that allows you to interact with the objects in the DOM. It can manipulate the DOM by selecting, adding, updating, and deleting specific elements. Additionally, ReactJS can change the appearance of objects, such as colour and font-size.

ReactJS enhances the process by using a Virtual DOM, which is a lightweight copy of the actual DOM. When changes are made, ReactJS updates the Virtual DOM first and then efficiently applies the minimal set of changes to the actual DOM. This approach improves performance and provides a more seamless user experience.

An example using Vanilla JS

I’m following along with Updating UI with JavaScript. For my own little exercise I am going to use Vite to create the project. You can check out the repo.

Vite (French word for “quick”, pronounced /vit/, like “veet”) is a build tool that aims to provide a faster and leaner development experience for modern web projects.

With the following code you can see the HTML in the DOM is different to the HTML served up because it is being manipulated with JavaScript.

<div id="app"></div>
<script type="text/javascript">
const app = document.getElementById('app');
const header = document.createElement('h1');

// Create a new text node for the H1 element
const text = 'Develop. Preview. Ship.';
const headerContent = document.createTextNode(text);

// Append the text to the H1 element
header.appendChild(headerContent);

// Place the H1 element inside the div
app.appendChild(header);
</script>

The code above is very verbose and in my mind is the main reason client side libraries like ReactJS exist today.

As the size of an app or team grows, it can become increasingly challenging to build applications this way.

With this approach, developers spend a lot of time writing instructions to tell the computer how it should do things. But wouldn’t it be nice to describe what you want to show and let the computer figure out how to update the DOM?

Declarative programming vs imperative programming

ReactJS is an example of declarative programming where you focus on what the UI should look like and less on how to achieve it. ReactJS uses a component-based approach to building your UI. When you create a piece of UI with ReactJS you encapsulate both the HTML and JavaScript in a single component like a button, for instance. ReactJS abstracts the manipulation of the DOM with the Virtual DOM. It takes over the role of manipulating the DOM — you can therefore focus on creating components (declarative) and not get bogged down specifying the behaviour of your DOM interactions. The end result is reduced complexity.

Let’s do some ReactJS!

In the tutorial Getting Started with ReactJS they use the hosted ReactJS libraries from a CDN. But again I am going to rather use Vite to create the basic ReactJS project for me.

I run the basic Vite command and follow the prompts. I’ll choose the TypeScript setup.

npm create vite@latest

I deleted the code in the App.tsx and added the following.

function App() {
return (
<h1>
Develop. Preview. Ship
</h1>
)
}
export default AppReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)

The code above uses something called JSX (JavaScript XML). JSX has become so prevalent across many frameworks — BunJS supports it out of the box for example. It provides an HTML-like syntax allowing you to embed JavaScript expressions within braces {}. Its specifically used within ReactJS to build components with the added benefit of being able to pass props. JSX is not valid JavaScript and requires a compiler like Babel.

With ReactJS and JSX you can declaratively build your UI without having to worry about direct DOM manipulation. It allows you to cut down on a lot of repetitive code.

Building on some more foundations

An interesting aspect to consider is state, as in application state within a ReactJS app. The UI acts as a piece of state — whenever there is a change in the state of your application ReactJS re-renders the UI. When building UI elements for a ReactJS app you create components. Components will invariably need some form of data to display — or props as they call it. The props or values in your component may change when a user clicks a button or enters information into a form. Understanding components, props and state are core concepts to ReactJS. Where it gets interesting with NextJS is the use of Server and Client components. But before we go there lets cover some additional ground. The whole idea behind ReactJS components is to think of your app as a set of LEGO bricks. The LEGO bricks are the components and when you combine them they form a bigger structure. Each piece of UI is self-contained and can be updated at the component level. The concept of having your UI split into modular components means being able to update or deleting components without affecting the entire application. It makes sense but in practice getting your code modular enough to not affect the entire application takes practice.

ReactJS components are JavaScript functions that return UI elements (using JSX). Let’s say your ReactJS app consists of a header. Here I am using TypeScript to define an interface.

interface HeaderProps {
title: string;
}
export const Header = ({title}: HeaderProps) => {
return (
<h1>
{title}
</h1>
)
}

In the NextJS learning material they create a header component and pass it to ReactJS’s render method. In my adaptation I am going to add the header component to the App.tsx.

import './App.css'
import { Header } from './Header'
function App() {
return (
<Header title="Develop. Preview. Ship" />
)
}
export default App

An important point to remember at this point is that all ReactJS component names must be capitalized to distinguish them from plain HTML and JavaScript. For instance <div></div> is a standard HTML tag. Custom components have a capitalized first letter.

Lets contrive the example a bit further. Lets say your page consist of a header, sidebar, and a footer.

export interface AppProps {
headerTitle?: string;
menuItems?: string[];
footerContent?: {
company?: string;
year?: number;
};
}
function App() {
const [appProps, setApprops] = useState<AppProps>({
headerTitle: "Develop. Preview. Ship",
menuItems: ["Home", "Services", "About", "Contact us"],
footerContent: {
company: "React App",
year: 2024,
}
});
const buttonClick = () => {
const incrementedYear = appProps.footerContent?.year ? appProps.footerContent.year + 1 : 0;
const currentApprops = {...appProps, footerContent: {...appProps.footerContent, year: incrementedYear}};
setApprops(currentApprops);
}
return (
<>
<Header headerTitle={appProps.headerTitle} />
<Sidebar menuItems={appProps.menuItems} />
<Footer footerContent={appProps.footerContent} />
<button onClick={() => buttonClick()}>Increase the year</button>
</>
)
}
export default Appexport const Sidebar = ({menuItems}:AppProps) => {
return(
<div>
<ul>
{menuItems?.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)
}
export const Header = ({headerTitle}: AppProps) => {
return (
<h1>
{headerTitle}
</h1>
)
}
export const Footer = ({footerContent}:AppProps) => {
return (
<div>
<p>
&copy; {footerContent?.year} {footerContent?.company}
</p>
</div>
)
}

The code highlights several key aspects related to ReactJS foundations. First the notion of nested components. The Sidebar, Header and Footer are nested in the App component. In the App component I initialize some arbitrary AppProps and pass it down to the nested components. Each of the components then use the AppProps to display certain pieces of information. I also added a button which when clicked increments the year to illustrate the use of state.

Closing remarks

I covered some ReactJS foundations in particular looking at the key difference between a declarative and imperative approach to developing user interfaces (UIs). I also did some very basic ReactJS to illustrate the use of props as its a building block for creating components in conjunction with state. I put the code from the basic examples on GitHub. Covering the foundations leads onto my continued journey with NextJS! Lets see what comes next!

--

--

Fredrik Erasmus

Software Imagineer. Eager to learn new things. Focused on solving problems. Mainly focused on ASP.NET Core, C#, Azure.