Hi and welcome to this new tutorial series on building AI applications using Next.js. In this first part, we’ll get started on the basics and setting up Next.js for our project.
What is Next.js?
Next.js is a powerful React framework designed to build fast, search-engine-friendly, and dynamic websites. It uses server-side rendering (SSR) and static site generation (SSG) for very fast load times and to improve search engine visibility.
Next.js also simplifies development with features like built-in routing, data fetching, and API routes. It has a strong and active community providing us with numerous libraries and tools for extending functionality. Overall Next.js is an efficient foundation for crafting modern web experiences.
Next.js also comes with a Node.js runtime. This means we can run server-side code and interact with databases, APIs, and other services. Javascript can be used both on the client’s browser for the front end and on the server side for the back end, basically allowing us to build full-stack applications within a single language and framework, which is pretty awesome!
As we write our code within one single Next.js project, the front-end stuff will get bundled and sent to the browser client of the user, while the back-end code will get executed on the Node.js server environment. This greatly simplifies and streamlines our development process!
Prerequisites
- Basic experience with Javascript is helpful. However, as Javascript is reasonably easy to read, familiarity with a different language like Python should also be sufficient.
- Some rough knowledge of basic HTML and CSS and how they work together to create web pages is going to be a big help to understanding and following along.
- Experience with React is beneficial but not required.
- For all the above requirements we will be covering the basics and whatever you need to know to be able to follow along, and take out some time to explain syntax and meaning whenever we first encounter something, be it some Javascript, CSS, or a React Component, so don’t worry if you are not yet familiar with them. Just know that there is much more to learn about these topics, but you can always dive in deeper afterward.
Setting up our development environment
First of all, we must make sure that we have Node.js installed on our computer, as this is the runtime environment that will take care of the back-end code etc. You can download Node.js from the official website nodejs.org:
Just run through the install wizard and you may get a popup question like this:
Personally, I’d just select yes. I’m not entirely sure if this is needed for Next.js specifically, but sooner or later you will probably need these tools anyway if they’re not on your system already. Just let that run until it finishes your installation.
After that, the script will start running and installs the necessary tools we selected in the checkbox earlier. It will skip over stuff already present on your system:
When that is done, we can continue. I’ll be using VSCode as my code editor, and while you are free to use your own, following along may be the easiest and most convenient if you use the same editor, especially as we’ll be using some specific extensions to make our life a bit easier.
Open up VSCode, go to the extensions tab on the left, and search ES7+
:
Go ahead and install this particular extension as it will help us out with some of the boilerplate code that comes with writing React components.
Next up search for Typescript
and find and install the following extension:
Typescript will allow us to work with typing in our code, and this extension will help us with syntax completions and such. If you’re not familiar with Typescript you’ll see how the very basics work as we go along.
Finally, search for Tailwind CSS
and find and install the following extension:
Tailwind CSS is a utility-first CSS framework that we’ll be using to style our components. It’s a little bit like Bootstrap with the utility class names so you’ll find the general idea quite familiar if you’ve ever worked with this. The extension will help us with syntax highlighting and such.
Again, if you’re not familiar with Typescript or Tailwind CSS, don’t worry. We’ll cover what we need as we go along so you can use this course as a basic introduction and then build on your knowledge afterwards.
Creating a new Next.js project
Now open up a terminal window and make sure you are in a folder where you are comfortable creating your new project folder inside. So this does not have to be the project folder, but the place for your project folder to go, as the install script will create the folder for you. Open up the terminal and start with the following command:
npx create-next-app@14.2.5
Normally you would run npx create-next-app@latest
, but for tutorial purposes, it’s best to stick to the same version I’m using so your experience will be as similar to mine as possible. npx
is a package runner tool that comes with Node.js (which we installed earlier), and it will download the latest version of create-next-app
and run it for you. This will create a new folder with the name of your project, and install all the necessary dependencies for you.
It will ask you several questions:
We type y
to proceed and it will ask us several questions. First I’ll use the name finxter_nextjs
for the project. Say Yes
to Typescript, ESLint (for code linting and catching errors), and Tailwind CSS (for styling). We will not be using the src/
directory, but make sure in the next question that you do use the new App Router
by selecting Yes
. There is no need for us to customize the default import aliases, so we’ll just select No
for that. Now press enter and it will install the necessary dependencies for you and you will see your project folder appear:
π finxter_nextjs
Running the project
There are a whole lot of folders and files inside this new project folder, but before we get into that let’s make sure our project runs correctly. Go back into the terminal window and run the following command:
cd finxter_nextjs npm run dev
First we navigate into the project folder (cd stands for change directory), make sure to use your own folder name if you chose a different one from mine. Then we run the npm run dev
command which will start up the development server for us. NPM is the Node.js package manager and kind of the equivalent of pip
for Python. We use it to run the run dev
command for this particular project. You should see a message like this:
> finxter_nextjs@0.1.0 dev > next dev β² Next.js 14.2.5 - Local: http://localhost:3000 β Starting... Attention: Next.js now collects completely anonymous telemetry regarding usage. This information is used to shape Next.js' roadmap and prioritize features. You can learn more, including how to opt out if you'd not like to participate in this anonymous program, by visiting the following URL: https://nextjs.org/telemetry β Ready in 3.8s
Go ahead and click the localhost URL link and you should see a page like this:
Congrats! You’ve successfully set up your Next.js project and it’s running correctly. The page you see now is just an example page that was installed with the project structure. It’s just a sort of quick-start example project that we can now edit to create our real project.
Project structure
So now if you open up the project folder you will see there are quite a lot of files here:
π finxter_nextjs π .next π app π node_modules π public π .eslintrc.js π .gitignore π next-env.d.ts π next.config.mjs π package-lock.json π package.json π postcss.config.js π README.md π tailwind.config.js π tsconfig.json
That is a lot of folders and files! Let’s go over a rough description of what each is for and we’ll look at them in more detail when we need them. The π .next folder is just for the Next.js framework itself and we don’t have to worry about it, π app is where the components of our app will go, π node_modules is where all the Javascript libraries/dependencies for the project are installed (there will be a lot of them in there), and π public is where we place static files like images.
Here are the files and what they are for:
- π .eslintrc.js is a configuration file for ESLint
- π .gitignore tells git what to ignore when committing our project to for instance GitHub
- π next-env.d.ts is a typescript file for Next.js that we don’t have to worry about
- π next.config.mjs is a configuration file for Next.js
- π package-lock.json is a lockfile for your dependencies, so it saves a list of all the libraries that our project is dependent on and which are installed in the
node_modules
folder. This is a very long list but this is kind of normal for Javascript. There is a running joke about a list of the ‘heaviest and densest objects in the universe’ where thenode_modules
folder of a javascript project ranks higher than a black hole for a reason. - π package.json is the main configuration file for your project
- π postcss.config.js is a configuration file for PostCSS, which is a tool that will transform our Tailwind CSS into standard CSS for the client’s browser and automatically handle various other CSS-related things for us.
- π README.md -> just some basic instructions.
- π tailwind.config.js and π tsconfig.json are just config files for Tailwind and Typescript respectively.
Phew! That’s a lot of stuff, but as you can see many of them are just configuration files that we don’t have to worry about most of the time unless we want to change some settings. We’ll mostly be working in the app
folder where we’ll be creating our components and pages, but having a rough idea of what everything is makes it look a lot less scary!
The app folder
So let’s turn our attention to the app
folder instead:
π app π favicon.ico π globals.css π layout.tsx π page.tsx
We have a favicon
for our site icon, a globals.css
file for global styling variables, and a layout.tsx
and a page.tsx
file for the main layout and main page of our site respectively. The .tsx
extension is for Typescript files that also contain JSX code, which is often used as part of React. So it’s just Javascript with types and some extra syntax for React components.
Go ahead and open up the layout.tsx
file and let’s see how this works:
import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css";
Keep in mind that this layout file is supposed to have the general layout of our site, so anything that is very repetitive across many pages. The first line imports the Metadata
type from the next
library, which is a type definition for the metadata of a page.
The second line imports the Inter
font from the next/font/google
library, which is a Google font that we can use in our project. Finally, it imports the globals.css
in the same folder which has the global css settings.
const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", };
The inter
variable is set to the Inter
font with the latin
subset. This may seem a bit odd at first, but the Inter
object that was imported is not the font but an object to work with it, and in this case, we limit the subset to latin
characters as we won’t need more for now.
The metadata
object is set to an object with a title
and description
key. This is the metadata that will go into the generated HTML and is also used for SEO purposes. Feel free to change the title and description to anything you like.
export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en"> <body className={inter.className}>{children}</body> </html> ); }
This is the main layout component that will wrap around all the other components of our site. So a Next.js or even a React or simple HTML project for that matter is made up of components that are nested inside each other. This RootLayout
component is the top-level component that will wrap around all the other components.
Don’t worry too much about the exact syntax, most components will be easier to read than this, but here is what is going on in nitty-gritty detail as this can look quite scary if you haven’t seen this before. Feel free to skip a bit past this explanation if you are very familiar with React already:
- The
RootLayout
function, which is also thedefault
export of this file, takes achildren
prop. The reason for the{}
around{children}
is that we are destructuring thechildren
prop from the props object that is passed to the function. So actually there is an input object, say we named itprops
and it has a key namedchildren
inside. As referring toprops.children
is a bit awkward the{children}
destructures thechildren
key from theprops
object so we are left simply withchildren
. - The part after the
:
is the type declaration for Typescript. This is kind of like a type hint in Python except the degree of enforcement will be stricter than you’d be used to with Python which is mostly for show and linting purposes. Here we are saying that theRootLayout
function takes an object with achildren
key that is of typeReact.ReactNode
. This is a special type that React uses to denote that thechildren
prop can be any valid React node, which is basically anything that can be rendered in React. This is wrapped further in aReadonly
type. Don’t fret too much about all the<>
and{}
syntax, you’ll get used to it step by step. - export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) is in essence just the function input argument and type declaration + the name for the function, nothing more!
- After this follows the actual code that this function contains, namely
{ return ( HTMLTAGSHERE ); }
- All this function will do right now is return an HTML tag with the language set to
en
and a body tag with theinter.className
. The body is the tag that holds all the content of the page and by giving it aclass
ofinter.className
we are applying theInter
font to the whole page. Theinter.className
is simply a property that theInter
object has that will give us the class name for the font. - The only thing inside the body tag is the
{children}
prop. Note how just like theinter.className
it is wrapped inside{}
. This is because we are using JSX syntax, which is a mix of Javascript and HTML. The{}
is used to denote that we are switching from Javascript to HTML and vice versa. So here we are switching from HTML to Javascript to insert thechildren
javascript object into the HTML.
This is just a component that receives the content to go inside the page under the name children
, and wraps some HTML tags around it, inserting the children
in the middle. The power of this layout page is of course that we can add other stuff to this or change the font and it will wrap around all the pages of our site, so we can have a consistent look and feel across all pages.
Now open up the page.tsx
file, and select anything in between the <main>
and </main>
tags, deleting all of it (it’s just example content). You will be left with only this:
import Image from "next/image"; export default function Home() { return ( <main> </main> ); }
All this is just a function, or a component in React terms, that returns some JSX code, which is just HTML inside Javascript. As we just cleared it out it’s empty now, so go ahead and place some text in there, and also delete that image import for the sample image since we don’t need it.
export default function Home() { return ( <main> <h1>Hello Finxtery FinxyWorld</h1> <p>Building AI applications using Next.js.</p> </main> ); }
The awesome thing about this npm run dev
development server which is still running in the terminal is that it has hot reloading. This means that as soon as you save the file, the server will automatically update the page in your browser. So go ahead and save the file and you should see the changes reflected in the browser:
Now we can see that we obviously have some styling issues here, but we have a canvas to start building on! By the way, if you’re not in dark mode, you will probably see something more like this:
With this kind of weird gradient thing going on! So let’s take a look at the globals.css
file to fix our styling up a bit. First of all, let’s take a moment to understand what is going on here by dissecting the contents because I just hate these tutorials where you follow along and copy-paste stuff without understanding what is going on and what everything even means or does.
@tailwind base; @tailwind components; @tailwind utilities;
Tailwind CSS Directives:
@tailwind base;
: Imports Tailwind’s base styles, which include modern browser normalization (resetting default styles like margins, paddings, etc.).@tailwind components;
: Imports Tailwind’s component classes, which are predefined classes for UI components.@tailwind utilities;
: Imports Tailwind’s utility classes, which are single-purpose classes designed to manage layout, typography, colors, etc.- We’ll see how we can use these Tailwind classes for quick styling later on.
:root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; } @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; --background-start-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0; } }
Custom CSS Variables:
- The
:root
selector selects basically everything to apply these styles to, defining global CSS variables (--
prefix) for light mode, including colors for the foreground and background gradients. - A media query for
prefers-color-scheme: dark
overrides these variables for users with dark mode enabled on their device, setting the foreground to white and the background to black.
body { color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, transparent, rgb(var(--background-end-rgb)) ) rgb(var(--background-start-rgb)); }
Body Styling:
- Applies the defined CSS variables to the
body
element, setting the text color (color
) using thevar()
method with the variable name passed in, and a linear gradient background (background
). The gradient starts transparent and fades into the end background color.
@layer utilities { .text-balance { text-wrap: balance; } }
Custom Utility Class:
@layer utilities
defines a custom utility class within Tailwind’s utility layer. The.text-balance
class is meant to apply thetext-wrap: balance;
property which tries to keep lines of roughly equal length when wrapping them around.
Now that we have an understanding of what everything is and does, let’s get rid of most of it as we don’t need it:
@tailwind base; @tailwind components; @tailwind utilities; :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; } /* @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; --background-start-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0; } } */ body { color: rgb(var(--foreground-rgb)); padding: 1rem; }
So I commented out the dark-mode part, as I’d like my screenshots and recording to match yours and half will be using dark-mode and half will not, giving everyone a different experience. As I personally love dark mode I left it as a comment so you can easily re-enable it later on.
I also removed most of the body
element styling to get rid of the gradient background which was repeating on us but added a padding
of 1rem
to give us some space around the text. In case you’re less familiar with CSS, padding is basically some white space on the inside of an element so it is not squashed up against the edges.
The amount of padding, 1rem
, is a relative unit that is equal to the font size of the root element, so kind of like the base font size for the page, and if you make the base font size of the page bigger then the rem
unit will grow along with it, keeping the ratio the same.
I also removed the @layer utilities
part as we don’t need that custom utility class for now. Now if you save the file you should see the page looking a bit more normal. Notice the page in your browser will change without even having to refresh the page, that’s the hot reloading in action!
Ok, now that we have the basics out of the way, we can start building something, so I’ll see you back in the next part where we’ll start playing around with our blank canvas!