How Next.js 13's CSS modules & Loading State Feature Can Improve User Experience
In this series, we’re continuing through the basics of learning the new features of the static site generator, Next.js 13.
Introduction
In the previous tutorials, we learned how we can construct pages and layouts in the new Next.js 13 app directory, and we'll also looked at some basic routing and Linking.
Today we'll dig deeper and look at how CSS modules is applied with this starter project we've been using. We also have a quick look at Loading state using Next.js 13.
Prerequisites
If you intend to follow along with this tutorial on your system, please ensure you have the following:
A basic understanding of working with the terminal.
A Laptop or PC with Windows installed
An Internet connection
Git is installed on your local machine (optional). For more details on accomplishing this, review Installing & Setting Up Git on Windows.
A GitHub account. For more details on accomplishing this, review How to get started with Git and GitHub
The latest version of Visual Studio Code is installed on your machine.
Basic knowledge of HTML, CSS, JavaScript, and React.
Note: This is a follow-up tutorial, it does require you to have created the second tutorial where we covered Routing, Layouts & Links, as covered in the article : Take Your Next.js 13 App to the Next Level with New Routing Enhancements, Dynamic Layouts, and Efficient Links to make the most of it.
Let’s get started!
Creating a Starter Project
You can see we've got Visual Studio Code open, where our completed code for the previous part nextjs-two of this beginner series is, which is essentially the modified starter project we've been doing for the last two tutorials.
Let's get started by making a copy of nextjs-two and renaming the file to nextjs-three inside our nextjs-series directory.
I'm going to open up this nextjs-three directory with Visual Studio Code and we can see everything that was installed from nextjs-two is over here in the file tree, but we need to change some of the contents of the package.json
file,
and the packages.lock.json
file as well.
Accessing the Development Site
So let's go ahead and open a terminal window once again, and I'm going to type in the following:
npm run dev
This will start our basic application. We can see it starting up, and we see all of this information here in the terminal, but here is the URL we can press control and then just mouse click to follow that click and Voilá! it's going to launch that in Chrome.
ready - started server on 0.0.0.0: 3000, url: http://localhost:3000
Once the development server is running, you will be able to browse to http://localhost:3000.
Here is our page, visible in the browser.
We have the full page that has started, and this was that page.tsx
that we saw inside of the app directory —Now as cool as this looks, let's go ahead and make some more changes.
Let's just delete what's inside the <main>
element down here:
And replace that with the following:
<h1>Hello Welcome to Next.js 13</h1>
<Link href="/about">Go to About Page</Link>
The page we just edited, page.tsx
of the app directory will look like this:
If we can look at the application running on http://localhost:3000
we can see that now we have a different Homepage:
Note: Just underneath the Hello Welcome to Next.js 13 -our link that says: Go to About Page is at the right, but we can go ahead and click that, then it takes us to the about page that has the site Navbar, it has the About NavBar it still has the h1 that says About and Link to Home page so we can go back and forth now with links rather than using our back and forward buttons from the browser Tab.
CSS modules
CSS modules in Next.js 13 allows developers to write modular and scoped CSS by automatically generating unique class names and localizing CSS styles. It enables developers to define CSS classes in a single file and import them into the components where they are needed. This way, the CSS styles are scoped to the components, eliminating global styles and reducing the risk of style conflicts. Additionally, Next.js 13 now supports CSS modules for both client-side and server-side rendering, providing a consistent UI experience throughout the application.
So continuing with our follow-up tutorial, I previously mentioned that CSS is the reason this about link is down there.
Let's change that and briefly look at how some CSS is currently applied in this modified starter project.
Right now this class{styles.main}
that we see in page.tsx
of the app directory is applying that beginning CSS.
We can go ahead and remove that without removing the <main>
element of course.
<main className={styles.main}>
After we save, that's definitely going to change what is applied in the home page but we can see what that starter project did and that is: import styles from "./page.module.css"
so it made some Styles inside of this module -some CSS Styles specifically for this page.
And then there are also the file globals.css
that's applied to the entire site.
We could also create some styles that are applied just to the layout for a specific level or path in the directory tree like our about path.
Continuing with the about directory, let's create a new file called: styles.module.css
-we're creating a CSS module.
We're going to create a class called .main
, and then inside of this main class we'll have the following styles:
.main {
min-height: 100vh;
display: grid;
place-content: center;
background-color: #333;
}
Let's apply this to the layout.tsx
inside the about directory.
So any page, any component in the about path, what I mean is the about page but also any children would receive these Styles and to do this, we need to import Styles at the top.
import styles from "./styles.module.css";
And then after we've imported those we just need to apply those, and we can do that with this <main>
element:
<main className={styles.main}>
The nice thing about these modules is you could have a main class with the same name inside of the page.module.css
and then have essentially the same name at two levels, but they're not applied the same way because they're just applied to the component in that class, however, you would have to be careful if you were using globals and applied those.
Remember we removed the <main>
element with a className
just previously from the file page.tsx
of the app directory.
Let's put it back again:
<main className={styles.main}>
Let's go ahead and see what was applied on our home page:
We can see that we have applied our <main>
element with a class that is entirely different as it was before.
Now after we click on Go to About Page it shows a darker color, it's centered, our header and our link are together unlike the other main class that was applied to the parent.
We can see how the module CSS works and applies only to that level or that page, so this is at the page level when we have page.module.css
we import that and apply it just to that page, now we did this differently for the about -we applied it to the layout.tsx
This would apply essentially to all {children}
, because the children are nested inside of this <main>
element that got the class.
Just so you understand the different levels we're talking about page level or we're talking about layout path level or of course, you go to the very top and you have also the global level that applies to all.
When we go back to the home page, the app.tsx
and remove that main class -we can see the difference as well, now everything shifts to the top because those styles are no longer applied to our parent page.
Loading with React
A loading.js
or loading.tsx
file is a JavaScript file that is used to create instant loading states for web applications, built on top of the React library's suspense feature. The suspense feature in React allows developers to define components that can delay rendering until some data or resource is ready, which can help improve the user experience by showing loading indicators or placeholders while data is being fetched.
The loading.tsx
file typically includes helper functions or components that can be easily used to create loading states for different parts of a web application. For example, it might include a component that shows a spinner animation while data is being fetched from a server, or a function that can be called to delay rendering a component until its data is ready.
By using the loading.tsx
file, developers can easily implement efficient and user-friendly loading states in their web applications, without having to manually write complex code to manage asynchronous data fetching and rendering.
Now let's move on with Loading. We can add more files here that some you don't even see that weren't provided with the starter project but they can really help the project out and one of those is a loading state, so once again in the about directory let's create a new file and we just name it: loading.tsx
.
Inside of this loading.tsx
we'll type RFC and get our loading functional component. Once again make that a capital L for the name of the component and then we are returning a loading state, instead of what is currently being returned we're going to put a <h1>
inside of this we just type the word Loading with some dots.
export default function Loading() {
return <h1>Loading...</h1>;
}
Another great thing is the loading file can create instant loading states built on Suspense, under the hood.
We will be able to briefly see this loading message when we navigate between those two pages.
First to see this in action we'll open up our Dev tools -when we are on the Home page, we just right click with our mouse and choose ,.
Next under the Network Tab we have a option that says Slow 3G. We select that option to see the Loading state just changing more slowly.
Finally, we minimize the Dev tools then we'll go to the home page first and remember the home page doesn't have a loading state -go to the about page to see the loading message we did just briefly.
Click on Go to About Page and see it in action.
Conclusion
In this article, we learned how to use the CSS modules. We also saw how to apply react suspense and loading states.
The patterns and conventions we learned in this article are still experimental features that Next.js 13 is implementing while moving forward, however, they are already very useful, and we can already start using them in our projects.
Next.js 13 is constantly incrementing these new features and adding some new ones, so I'll try to cover those if they continue during this beginner series; however, I prefer to publish the new features rather than the previous ones, because this is the direction that Next.js 13 is headed in the future.
What's next?
During our upcoming part 4, we will delve into the topic of Error Boundaries. It is important to prioritize progress rather than perfection, as even small advancements made each day can lead to significant results.
Thanks for the read! Now go practice & build something awesome!
Subscribe to my newsletter
Read articles from José Horta directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
José Horta
José Horta
Full Stack Web Developer based in Portugal. Experienced in designing UI Web Components & developing .Net & React Applications.