Next.js has become well-known for its ability to build fast, reliable full stack apps.
And with good reason. It's developer experience is top notch and its wide range of features help to easily build performance-first apps while being able to focus on the unique challenges of your app.
With Next.js 15 and React 19 coming soon, its more important than ever to make sure you have a full understanding of now only how Next.js works, but how the newer React features can help you build amazing experiences.
This course will help you build a Full Stack Invoice app using Next.js 15.
Next.js has become well-known for its ability to build fast, reliable full stack apps.
And with good reason. It's developer experience is top notch and its wide range of features help to easily build performance-first apps while being able to focus on the unique challenges of your app.
With Next.js 15 and React 19 coming soon, its more important than ever to make sure you have a full understanding of now only how Next.js works, but how the newer React features can help you build amazing experiences.
This course will help you build a Full Stack Invoice app using Next.js 15.
Not only will we learn the intricacies of Next.js 15 and React 19, we'll also learn how to:
Design responsive components using Tailwind and shadcn/UI
Add authentication with social login, organization support, and MFA with Clerk
Create and manage databases and relationships accross tables with Xata
Query and wrangle data on a Postgres server (Xata) with Drizzle ORM
Process payments for invoices using Stripe
Build custom email templates in React with React Email
Send transaction emails with Resend
Deploy your app to Vercel
And looooots of important concepts in between
Ready to get started? Let's dig in.
Learn how to build a Full Stack Next.js Invoice app using modern web development tools. In this video, we'll walk through what you can expect out of this course including what tools we'll use and how to get started.
Create a new Next.js app using Create Next App.
Using Tailwind and shadcn/ui to create a new dashboard page where we'll get started with our invoices.
Forms are a critical part of being able to create new invoices. We'll see how we can add a new form to our web app.
The first step in setting up a new database is installing and configuring our project tools. Here we'll walk through setting up and configuring Xata and Drizzle ORM.
It's important to consider what information will go into a database before designing a table. Here we'll write our first schema for our Invoices.
Now that we've written our database schema, we can push our schema to the database itself. We'll generate and run migrations using Drizzle Kit and push them out to Xata.
Service Actions are a newer feature of React and Next.js that allow us to run server-like code from within components. We'll see how we can hook up our form with server actions to create a new invoice.
When building web apps, we want to make sure that our project can function in environments with and without JavaScript, which is where Progressive Enhancement comes in to have a baseline without JavaScript, but improve the experience where we can when JavaScript is enabled. We'll see how to do this natively with React 19 and with the new Next.js 15 Form component.
Now that we have data in our database, we can start to list out our invoices so we can explore our data in the UI.
Here we'll create dynamic routes in Next.js to view individual invoices.
Building a good experience includes handling errors. We'll see some ways we can take advantage of React and Next.js APIs to manage our error states.
When creating invoices, we're dealing with sensitive data, meaning we need to make sure we lock down our app. Here we'll use clerk to add social login using Google.
Adding log in capabilities is just the first step to securing an app, we need to make sure the pages are actually protected. Here w'll use Clerk Middleware to prevent unauthorized access to our invoices.
Now that we're managing authentication, we need to integrate common UI patterns to make it easy to navigate around the app and manage authentication state. Here we'll add a header and footer to the app using Next.js layouts.
Clerk comes with built-in login and signup pages, but they're hosted on Clerk. We can instead set up our own self-hosted pages so we can continue the same experience as our users are working through the authentication flow.
With our new custom authentication pages comes even more customization options using Clerk Elements, a suite of components, which gives us full control over how we present our UI, while allowing us to easily interface with Clerk.
Depending on your security needs, sometimes social or email login isn't enough. We can add MFA or Multi-Factor Authentication to level up our security, requiring an authenticator app to log in.
Instead of using passwords to log in, we can use Passkeys, which bring strong security and an easy-to-use experience for our users.
Server actions are a great way to manage data, but they aren't secure by default. We'll make sure our server actions are locked down using Clerk authentication.
Whenever we create a new invoice, we want to make sure it's associated with a specific user. Here we'll add a relationship to our Invoices table to specify which user each invoice was created by.
Since we may have multiple users in our app, we don't want people to see other invoices they don't have authorization to see. We can restrict access now that we have the user ID relationship in our invoice table, to prevent anyone who didn't manage the invoice from viewing it.
Whether our invoice is paid, void, or uncollectible, we need a way to update the status. Here we'll use server actions and a dropdown UI to allow our invoice manager to update the status of an invoice.
When triggering changes on a database, it can take some time to make the trip to the database, make the change, and back. Through that time, our UI may look slow and unresponsive, but instead, we can use Optimistic UI patterns to immediately reflect the change, and make the change in the background, or reverse if it fails.
In addition to change the status of an invoice, we need a way to flat out delete an invoice. Here we'll see how we can delete table rows for allowing our users to delete an invoice.
You can't undo a deletion, but what you can do is put an extra confirmation step, to make sure someone didn't accidentally click the wrong thing. We'll use a modal confirmation box to give our users the opportunity to confirm (or cancel) their deletion of an invoice.
When creating an invoice, each one will be associated with a customer, which we'll need to managing and contacting that customer for payment. We'll create a new relationship to a Customers table to create a good way to manage our data.
Now that we have our customer data associated with invoices, we need a way to access it. We'll see how we can join tables to easily access our relationship data in the app.
Sometimes invoices and data needs to be managed not only at a personal level, but at an organization level. Here we'll see how easy it is to set up organizations with Clerk.
Now that we're managing organizations, we need to relate creating an invoice to an organization much like we did with a user ID. Here we'll create the relationship in our Invoice table to an organization so we can later manage our data at the different levels.
Organization management brings a different type of data access, where someone could create a personal invoice but also an organization level invoice. Here we'll update our queries to make sure that we're querying for the right data depending on the account state our user is in.
The most important part of an invoice is accepting payments. We'll use Stripe to manage our payments and to get started, we need to install and configure Stripe in our app.
In order to allow our clients to pay, we need a public page available for them to access the invoice. Here we'll create a new public page where our clients can easily drop in to make a payment.
With our new public payment page, we need a way to kick off the payment session. We'll use Stripe Checkout and create a new payment session using server actions whenever our customer is ready to pay.
Once our user returns back from payment, we need to handle the invoice state, whether updating to "paid" or dealing with someone cancelling the payment. We'll use URL parameters and access the Stripe session to manage our ideally paid invoice.
An invoice a client doesn't know about is as good as no invoice at all. To fix this, we can create a new invoice email and to start, we need a template. Here we'll use React to create a responsive email template with ease using React Email.
Now that we have our template, we need a way to send it. We'll use Resend to send our transactional emails, including sending a notice to our client whenever a new invoice is created.
Our app is ready to go, all that's left now is to ship it! We'll use Vercel to deploy our invoice app to the world.
I hope you learned a lot in this course, here we'll wrap up and see what's next!
OpenCourser helps millions of learners each year. People visit us to learn workspace skills, ace their exams, and nurture their curiosity.
Our extensive catalog contains over 50,000 courses and twice as many books. Browse by search, by topic, or even by career interests. We'll match you to the right resources quickly.
Find this site helpful? Tell a friend about us.
We're supported by our community of learners. When you purchase or subscribe to courses and programs or purchase books, we may earn a commission from our partners.
Your purchases help us maintain our catalog and keep our servers humming without ads.
Thank you for supporting OpenCourser.