Let me tell you a story about a client.
This client was demanding. They wanted all the things, and they wanted them now.
Servers know this kind of client all too well. When the client comes in, asking for everything and the kitchen sink, a server just has to sigh.
"A lot of that is just going to go to waste, you know," says the server, softly.
Their comments go unheeded.
Does this client sound familiar?
This client is your Single Page Application (SPA).
There's a new client in town, though, and it only asks for what it needs.
This client is Next.js.
It also happens to be the server 🤔
Clients and Servers and Node Oh My
Starting with Next.js after working on SPAs can be challenging (at least it was for me) with the whole server thing going on, not to mention the node.js runtime 😱
It can feel like a steep learning curve, especially if you haven't worked much with Node.js, but at the end of the day remember that the client is still React! A majority of getting comfortable with Next.js, I've found, is understanding three things:
Next.js is powerful, and a lot of fun to build with once you get a handle on how it works 🏗 It combines some of the best features of traditional and single page web applications into a hybrid application.
If you want a refresher check out the high-level differences between the three application types!
⚠️ Expect that things will take longer, at least at first. If you're not learning at your own pace and have to make time estimates, remember to pad those so that you have the time needed to do things correctly the 'Next' way, and not just revert to SPA patterns on top of Next.
⚠️ Inevitably there will be places where adding functionality is just more complex when rendering occurs in both a server and a browser context like Redux or CSS-In-JS.
Review: SPA vs Next.js
In a SPA
Your code executes in the browser only
- The entire site code bundle is downloaded upfront
- You should not include sensitive information (API keys, passwords, etc...)
- Subsequent pages are all built from bundle code
- You have access to Web APIs and the DOM
In Next.js
Your code executes on the server first and in the browser second
On the server:
- Pages are pre-built and/or pre-rendered (more on that later)
- During API Routes & Data Fetching you can include sensitive information
- You do not have access to Web APIs & the DOM on the server
- You do have access to a whole new set of APIs in Node.js
In the Browser:
- The browser only gets the code it needs for the requested page
- None of the sensitive data from the server is available
- Subsequent pages make new requests to the server
- You have access to Web APIs and the DOM
This also means implementing functionality which may have had a common approach for SPAs, like Authentication, now has multiple approaches to choose from depending on a variety of factors related to how you design your application.
Execution Context
The considerations I want to focus on all revolve around the fact that there is now a server you have to deal with. At the risk of sounding obvious, I think it's hard to overstate how important this is.
The biggest gotcha?
Adding a server adds an execution context
Consider that you've essentially doubled the complexity of your application!
The context in which your code executes can be either the server or the client (browser). There may be code you write that can only execute in one or the other of these. Common examples are:
- The browser globals
window
&document
areundefined
on the server - The Node.js File system (
fs
) module isundefined
in the browser
Right off the bat do yourself a favor and create two utility functions to wrap code that should run in only one context:
export const isClient = () => typeof window !== 'undefined'
export const isServer = () => !isClient()
⚠️ Those methods aren't always necessary. The useEffect
(and useLayoutEffect
) React hooks will only run in the browser. The API Routes and Data Fetching methods will only run on the server.
⚠️ Don't leave unused imports; Node.js modules that are imported and unused will throw errors. The reference won't be removed before the code goes to the client and the browser will choke on the Node.js code.
Your Application is Starbucks
Before we go further, let's go back to our metaphor from the beginning.
Our client (a customer) walks into a Starbucks. Our server (a barista) will be ready to help with whatever the client wants.
Starbucks knows certain things about what the customer might want. Since there are things they can make ahead, they have a nice selection of canned beverages (like the DOUBLESHOT) that the customer can just grab and go!
The server has to do almost no work, huzzah 🥳
But Starbucks also knows that the customer might be picky (I'm looking at YOU Edward) so they're ready to make something crazy (the TikTok Special) on the fly.
The server has to do a lot of work, dang 😰
Finally, there might be some things the server can't provide. In this case the client will sneak into the bathroom with their hip flask (don't try this at home, always drink responsibly) and add their own whisky to the coffee.
The server will do some work, and so does the client 🍻
Can you guess who Starbucks is in the scenario above? It's Next.js 😆
The three scenarios above encompass the choices you have when building pages in Next.
Pages
Before building any pages it pays to step back and think about:
- where does your content come from?
- how often does your content change?
- how much of a page depends on certain content?
Answers to these questions will impact whether you want to implement the page via Static Site Generation (SSG), Server Side Rendering (SSR), or some combination of those mixed with client side rendering (CSR).
SSG
The Grab-and-Go: The fastest choice, Static Site Generation means little to no processing on the server & best for SEO & Core Web Vitals. The server only has to return pre-built, static content.
"Statically generated pages are still reactive: Next.js will hydrate your application client-side to give it full interactivity." - Next.js Documentation
Use this option with content that doesn't change frequently (or ever). Blogs, marketing sites, policy documents, and FAQs all fall more or less in this category.
This can get cumbersome on sites with many (thousands or more) pages, but can be mitigated to some degree with incremental static regeneration.
SSR
The TikTok Special: Server Side Rendering means rather than serving pre-built pages, the server builds the page when it is requested. The browser still gets static content, but data fetching & processing likely means longer time to largest contentful paint.
It's an excellent option for pages that depend on particular input data and where it isn't possible (or feasible) to statically generate all page combinations for given inputs.
Input data might be something like user details, purchase/order history, weather, time, or traffic.
CSR
The Hip Flask: Client Side Rendering can be added in addition to either of the strategies above. It can serve as a strategy to defer loading some page content so that most content can be ready faster via SSG or SSR.
The deferred content might be frequently updating/real-time data like a stock chart or chatbar, or content with a particularly long load time.
⚠️ Be mindful that if some content is not readily available there may be impact to SEO and issues with cumulative layout shift.
⚠️ Remember that page code may be executed on both the server and client! If you have any sensitive information in environment variables or other stores be careful that it doesn't get sent to the client accidentally.
API Routes
Let's extend our metaphor above even further! Consider an espresso maker, a beautiful and complex machine that you definitely do not want your customers, err I mean clients, touching.
To shield the clients from the complexity of the espresso maker, the client makes a request of the server. The server goes off and deals with all the complicated bits, and after a while the order is ready. Then the server gives a response, "Edward, I have your Venti Caramel Crunch Frappuccino!"
Until the response arrives, the client is free to doomscroll TikTok looking for cat videos and a new crazy coffee drink.
Your API Routes in Next.js mirror that interaction. They won't get you coffee, but if you build them right they can get you cat memes.
⚠️ Remember this is in the server context. You can use sensitive keys, secrets, passwords, and connection strings if required. You could interact with the filesystem, say, to pull in markdown documents for creating content. You could add an ORM like Prisma to interact with a database.
⚠️ Server-only context extends beyond API Routes. It also includes the Data Fetching methods getServerSideProps
, getStaticProps
, and getStaticPaths
. These methods are more specialized and I won't be going into more detail on them here, but the documentation linked in the pages section for each of the three types are great resources.
For reference, an API Route looks something like:
export default function handler(req, res) {
// Do lots of processing...call apis...access database...
res.status(200).json({ name: 'Next.js' })
}
Simple right? 😬
The Request
You're probably familiar with fetching data from APIs from the SPA architecture. Now you're on the API side of that transaction.
The request, or req
object, will have all kinds of information about the request that the client has made. This includes headers, referrers, browser information.
In API Routes there is also extra data added by Next which includes objects for cookies
, query
, and body
if that information is present.
If you're doing CRUD operations pay especially close attention to the method
the client is using, since you'll want to respond differently to different methods!
The Response
The response, or res
sends information back to the client. It's important to always send back a response or the browser request will never finish, drifting endlessly in the wind.
Similar to the req
object, the res
object in API Routes has some extra helper methods added by Next, which make building the response easier than default Node.js http.ServerResponse
functionality. It tripped me up when I saw these helpers used in tutorials but couldn't find them referenced in the Node.js documentation.
And with the response sent you're all wrapped up and ready to get on with building something new and exciting!
Further Reading
I hope that this has given you some new insight as you get started with Next.js!
If you're looking to set up Next.js beyond the basic scaffold, check on my tutorial on adding tests, linting, and absolute imports to a Next.js project.
Questions? Comments?
Follow me on Twitter @BenjaminWFox for more tech and leadership content, and reach out with any thoughts or questions!
Top comments (9)
Great Article, Ben!
I liked that you emphasised that now we're dealing with two execution contexts: the client and the server. For those who are more used to Single Page Applications, not being aware of that might cause some confusion.
Definitely, the shift in mental model is huge!
True, for that I am struggling as I need to give a breather and think differently from React. That is the reason I am still keeping my PHP site that requires SEO from moving to NextJS, but will move to next very soon :). Thank you for simplifying the concepts.
Great read. Good work Ben!
Great article!
Nice post, I just started a Next project this week and this was very helpful 😄
Glad to hear it! You're starting just in time to crank it up to 11 😁 nextjs.org/blog/next-11
thank bro . So what u think about nextjs in shopify bro ? thank for comment
Hi! To be honest, I know very little about shopify and e-commerce generally these days, BUT I know a lot of folks do use Next for ecomm AND Next has some great details on (what sounds like) a pretty seamless integration with Shopify: github.com/vercel/commerce. The Features section in that repo makes it sound pretty good :)