React and Angular are probably the most popular, competing frameworks right now. They are being used in thousands of commercial and non-commercial projects around the globe. If you ever googled for differences between both of them, you would learn that despite React being a wonderful framework, it's not entirely ready out-of-the-box experience. Angular still has a couple of aces in sleeves. But with Next.js React can overcame its shortcomings and perhaps end the old dispute "React vs Angular" in it's favour.
Why React is not complete
The quick answer: It was never designed to be a complete, big framework for all our developer needs. It started as just a view library - so just the V of the MVC approach. It quickly revolutionised web, gaining more popularity with new and fresh concepts like Flux, and then the Redux itself. More and more developers got excited about it and started releasing hundreds upon hundreds of middlewares and utilities to turn this primarily View framework into something more complete. Now, with its rich ecosystem of libraries, we can use it to create basically any app we can think of.
The problem is, that with all of the support from its community, it is tedious to start a new React project. Even with the usage of Create React App, you still need to think about and integrate:
- state management,
- middlewares to handle sideeffect if you happen to pick Redux,
- routing solution
- and many, many more...
It takes time and experience to set-up everything in an optimal way. No wonder that some developers prefer Angular. Once you install it, you're ready to start developing. Angular comes with a bunch of useful utils. Notably: Built-in router, state management, and basically convention-over-configuration approach. It just works.
We can't blame React for not having everything out-of-the-box, as it was never its intent. Luckly, there is Next.js to fill in the gaps and help us getting up and running in no time!
Discovering Next.js
So what is Next.js? It is basically a framework for React. If you consider React to be a framework (I do!), then it's a framework for a framework. It tries to address the issues that I mentioned before and deliver a ready-to-go platform. We can just install it and we have (almost) everything we need to start our project. It doesn't matter if it's a passion project done after hours, or a commercial project for a big client. Next.js got us covered. Let's take a look at its features.
Simple Setup
All we have to do in order to get a new app is simply typing the following in our terminal:
yarn create next-app
The creator will ask us two questions: Whats the name of your app and do you want to use a template. I suggest going with the default option for the latter one, although you can check existing templates if you're feeling adventurous.
After everything is done, we end up with the following structure
node_modules/
pages/
api/
hello.js
index.js
public/
favicon.ico
vercel.svg
.gitignore
package.json
README.md
yarn.lock
If we type the following, our app will start in development mode with hot reloading enabled! So cool! Type in the following to see your page live over http://localhost:3000
:
yarn dev
Tip: I suggest moving the pages/
folder into src/pages/
so we can keep all of our source files into the src
folder. Next.js will use src/pages
as well.
Routing
As mentioned before, Next.js has a pretty powerful routing included. What can be a bit inconvenient for newcomers is that it relies heavily on conventions rather than configuration. All JavaScript files placed in our pages/
or src/pages
will be mapped to an URL that user can access from the browser. pages/index.js
will be accessible at the page root, pages/users.js
can be seen at mypage.com/users
etc... For deeper nesting you need to utilize directories, meaning that pages/a/b/c.js
will turn into mypage.com/a/b/c
. Simple as that.
Obviously, without support for dynamic arguments in URLs, we could not go very far. Fortunately, Next.js utilizes the file naming convention to help us with that. Simply, to handle users/edit/{user-id}
URL, just create the file pages/users/edit/[userId].js
. We can access the userId
value by using the provided useRouter
hook:
import { useRouter } from 'next/router'
const Users = () => {
const router = useRouter()
const userId = router.query.userId
// rest of your logic
}
export default Users
Linking
As a small bonus to the routing, Next.js comes with built in linking solution. Available in the next/link
package, the component will help us link to our pages in more optimised way.
<Link href="/users/[userId]" as="/users/1">
<a>See the first user</a>
</Link>
By using the provided Link
in addition to the good old a
, we can make use of enabled by default prefetching, making our pages load faster. Moreover, even when working in Server Side Rendered mode, the Link
will allow us to actually render the page on the client side, making it kind of a smart SSR/SPA hybrid.
There is a number of options for the Link
, so we can very easily change its behaviour to use history.replace
instead of calling history.push
or just perform a shallow rendering (updating the URL without updating the page contents).
API support
This is where we dive into more advanced features. Next.js is more than purely Frontend framework. With it, we can develop also Backend endpoints very easily.
Following the convention of routing, every file placed inside the pages/api
directory will turn into an endpoint that we can call from the browser. The default file api/hello.js
shows us how simple it is to create working endpoints returning JSON data:
export default (req, res) => {
res.statusCode = 200
res.json({ name: 'John Doe' })
}
From here, nothing stops us from doing our backend logic, like querying a database. We need just to install our favourite ORM and we're ready to go.
Server Side Rendering
This was one of the features that blown my mind. Next.js comes with an excellent support for SSR! I was actually in a project, where client decided, that they want to have SSR enabled. But we developed everything as client side rendered page. Luckly, Next.js was here to help us making the switch quite quickly.
As an example, let us consider this very simple, completely client rendered page:
// pages/todo/[id].js
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router'
const Todo = () => {
const [data, setData] = useState(null);
const router = useRouter()
const id = router.query.id
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/todos/' + id)
.then(response => response.json())
.then(json => setData(json))
}, [id])
return <div>Todo - {data ? data.title : 'loading...'}</div>
}
export default Todo
In oder to convert it into a fully server side rendered page, we need only to export additional async function named getStaticProps
and move our data fetching logic there.
// pages/todo/[id].js
import React from 'react';
const Todo = ({ data }) => {
return <div>Todo - {data.title}</div>
}
export const getStaticProps = async (ctx) => {
const id = ctx.params.id;
const data = await fetch('https://jsonplaceholder.typicode.com/todos/' + id)
.then(response => response.json());
return {
props: {
data
}
}
}
export default Todo;
We have just turned our CSR page into a fully SSR page. That's incredible simple!
Static Pages Generator
Sometimes we need just a simple, static pages generated with no need for node.js server. In a very similar manner to the SSR, Next.js allows us to quickly create statically generated pages. Let's consider the SSR example - we need only to export additional method called getStaticPaths
that will tell Next.js what IDs are available.
Obviously, if we're generating site based on DB or some CMS, we need to retrieve all of the valid IDs here. At the end, simply return the object with all IDs. The whole code for a statically generated page is as follows:
// pages/todo/[id].js
import React from 'react';
const Todo = ({ data }) => {
return <div>Todo - {data.title}</div>
}
export const getStaticProps = async (ctx) => {
const id = ctx.params.id;
const data = await fetch('https://jsonplaceholder.typicode.com/todos/' + id)
.then(response => response.json());
return {
props: {
data
}
}
}
export const getStaticPaths = async () => {
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } },
{ params: { id: '3' } }
],
fallback: false
};
}
export default Todo;
Once we have all of the pages prepared this way, we can simply call the next build
and next export
commands. Lo and behold, our static pages are generated! What I find even more impressive, is that almost all of our routing features (like prefetching) will work in static pages as well.
TypeScript support
If you, just like me, prefer to have types in your project, then Next.js is perfect. Though it does not get generated as a TypeScript project, it can be easily converted to one. All we have to do is create an empty tsconfig.json
file in the root directory and run our app. Next.js will fill the config with its initial configuration and will be ready to work with our TypeScript code. As simple as that!
Alas, There are small caveats. Firstly, it is better to not change the existing properties in tsconfig.json
. For example, in one project I tried to disable the flag skipLibCheck
, but that caused the compiler to rise an error in one of the Next.js dependencies. So I strongly advice not to change the existing config. Adding new properties is cool, though!
Secondly, the documentation is mostly written for good ol' JS. That means it might be sometimes problematic to find the type of a param for function. For instance, let's take a look again on the example from API support:
export default (req, res) => {
res.statusCode = 200
res.json({ name: 'John Doe' })
}
We need to dig around in the docs, to figure out that the req
object is actually of NextApiRequest
type while res
uses NextApiResponse
. Not a deal breaker, but it's a bit annoying to look for the types.
Downsides
Next.js, as everything in life, is definitely not perfect and has its own shortcomings. Have you noticed already that I haven't mentioned anything about state management? It's because Next.js, as packed with features as it is, does not provide us a built-in state manager. It is a kind of a bummer, that in all it's glory, with ready-to-go attitude, there is no state management.
But I guess it makes sense. Lately state management in React apps became a bit of a controversial topic. There are a lot of people saying Redux is great (including me, but I also acknowledge its flaws). On the other side, there are people saying MobX is the way to go. Finally, there are devs who would argue, that Context API is all we need, or something exotic like unstated-next
can be used (I don't recommend that one). With all those divisive opinions, it's no surprise that Next's devs haven't just picked one solution. Additionally, to be honest, with such a versatile tool, it would be probably hard to find an optimal solution.
But if we really need a state manager in our app, there are a lot tutorials on the web showing how we can quickly add Redux or MobX.
Another (albeit small) downside of Next.js is lack of out-of-the-box support for any of CSS-in-JS technologies. We can use CSS and SCSS right from the get-go. But when it comes to more modern styling approaches, we need to add some code. It's not a lot, though, and there examples linked in the official docs (here).
Summary
As we can see, Next.js is a great and really versatile framework for React. It provides a well configured, working out-of-the-box environment for creating basically ANY web app. Basically, Next is perfect for Single Page Apps, Server Side Rendered pages, Statically Generated pages or anything in between. With API support we can use it to create a complete pages with Backend logic. The only really big downside, is that there is no built-in state manager. Apart from that, it's got everything we need to create new web apps in no time.
In conclusion, I believe it's the most complete React experience out there. Next.js provides all features that just pure React is lacking of, making it feature-ready setup to face Angular in the "React vs Angular" debate. If React is ever to win said dispute, it will need a solid framework to do so. In my opinion, Next.js is exactly that, an incredible environment for modern web app development.
Top comments (35)
I have worked with NextJS and Redux, Saga, etc for years. And to be honest, the future of React is never be NextJS. It should be Static Site Generator (SSG) like Gatsby instead. I can give you lots of reasone, but the most important thing is: SSG makes frontend developer totally independent to backend/server. Frontend developers nowadays don't just only need good framework, they also need good Development Experience.
Anyway, thanks for your passionate post.
A bit on a tangent here - It's funny how the technology comes full circle. Back in a day all we had was static pages (although not generated). Then we had server side pages, finally SPA and now we go back to static pages :-)
Yes agree :) but frontend development now is very interesting journey. I feel lucky when I have faced the time working with static pages from scratch, only with HTML, JS, CSS till now. That's a long but very fun adventure.
But nextjs provides SSG capabilities as well. I just think that gatsby locking you to graphql not a good thing :(
Next.JS doesn't lock you with dynamic rendering and Gatsby doesn't lock you to GraphQL (gatsbyjs.org/docs/using-gatsby-wit...).
SSG is good for 99% of the cases, in some cases you're better off with a dynamic rendering though, i.e. if your data changes a lot above the fold AND you need search indexing on that data frequently. Some notable examples are product catalogs that change a lot, like real estate. Product pages (if you have over 1000000 product pages you won't do them all in build time, some form of dynamic rendering will be needed even if on the spot snapshot rendering + caching for some time). We need to know when to use which.
Yeah correct, but Gatsby team is trying to improve for that case with gatsby cloud.
I mean...that, for me, gatsby is more opinionated than NextJS. I just think it's easier to configure NextJs than to do that with Gatsby. Now (jokes aside) both try to lock you in their cloud service. I find serverless component the best way to do it on AWS for NextJS outsite Vercel service. But that is because I lack a little bit of knowladge to set up that structure for myself :(
I see, everything has its own pros and cons. If you familiar with NextJs, just stick with it as long as it resolves problems :D
At the end we are all stating our opinions :) but it's good to see how others see :) nice talk 🙂
Nice :)
"totally independent to backend/server", could you elaborate on this? Because as I see it with SSR, React would still perform the same requests as with CSR but before sending it to the client?
I'd love to hear! 😉
Sure. NextJS need A WEB SEVER for it, usually Express. But when working with Gatsby, you totally forget about Express or any web server. A real use case is testing frontend on production. With NextJS, you still need to deploy it somewhere to test the production, but with Gatsby, you can totally test production on your server. That sounds like a true "serverless" approach. And Gatsby makes the development experience from development to deployment seamlessly.
Any feedback will be highly appreciated :)
Actually, you can use
next export
and just host your app anywhere that serves static files. See nextjs.org/docs/advanced-features/...Yes that's the new NextJS feature. But for me, it sounds like they realized that the "static file generator" should be the future, that's why they introduced it.
Most of their docs make it seem that way, recommending SSG in most use cases. I just mentioned it because you said that Next forces you to use a backend, but as long as you only use SSG you're good to host your site on e.g. GitHub Pages
Thank you for your reply. That's really a good news for NextJS fan. Let see for a while how both NextJS and SSG grow.
If you use a SSG, you don't need something like React. Because they are complete opposites of each other.
Yes but SSG I mean, a higher level of frontend development. You can choose whatever you want.
obviously scientist
The convention what is considered a framework is really blurry. I remember working in Java more then a decade ago. Everything there was a framework, including a library for making pretty URLs (not even a full routing) was considered a framework. So that's why I wrote, that people can consider it a framework or not.
Next is awesome, but I absolutely HATE that the Next Router is so tightly embedded. I know it's not the same, but it feels too much like RoR forcing Active Record on its devs. As someone who does a great deal of single codebase dev with React Native/RN Web particularly with React Router (web and native) instead of React Navigation, it makes this particularly frustrating.
Compare, for example, how easy it is to integrate React Router with Gatsby. I know, SSG vs. SSR (with optional SSG), but still.
Shout out to After.js for shaking loose from Next Router and basing on React Router instead... Now if it would just provide static site gen like Next.
Agree, the routing is quite strange. I guess I never had an issue with it, but I can understand that it can be not versatile enough for some cases.
It's not bad, it's just... Rigid.
React is very often referred to as a framework, and IMO it fits that description very well. The library-vs-framework argument is as old as software, but the best test I've ever heard (can't remember where now) is this: if you mostly call its functions, it's a library; if it mostly calls your functions, it's a framework. I would argue that React more closely fits the latter definition, while e.g. Lodash and even jQuery more closely fit the former.
I don't know of any definition that says that a framework must have view, routing, state, and auth, though I'd be interested to read about that perspective (genuinely 🙂). With that said, React does at this point have state management built in with the
useState
anduseContext
hooks. It's not a particularly advanced state system, but it's there, and I've built entire fully functioning applications in React with nothing more than those. And while it's true that it doesn't ship with routing out of the box, React Router is practically a part of the family with how popular it is; and moreover I don't think a complex router like that is necessary for all projects, and the ability to plug in different routing systems should not lower the status of the thing they're plugging into. And regarding auth... That's usually a server side thing, no? Do other UI frameworks have auth as a major component? (Honest question!)@dotintegral not to crap on your post. It's great. Sincerely.
No problem at all! It's what we do here, share opinions in order to figure out the best solutions for ourselves :-)
Personally, one of the things that sold me on React was that it was a view library (though with context, that's not entirely true anymore) rather than a "throw the whole kitchen sink at everything" complete MVC framework like Angular, while still keeping me from reinventing far more wheels than vanilla JS or smaller libraries, and with corporate backing and a massive community like Angular, and tons of community bits and pieces I could add on only as needed to further avoid reinventing wheels.
I can't fault Next for being opinionated, as many of my solutions are extremely opinionated. It's just that Next's opinions get in the way of many of mine.
Next.js is definitely awesome. With the latest updates and features added it takes the cake over any option currently out there. Like you mentioned, it has its downsides but I think the great majority are positive aspects like on a 80/20 ratio.
React is a state management library, and in my opinion by far the best one.
What?
What what?
Hey, great article.
Wanted to point out that Next does support CSS-in-JS out of the box with Styled JSX: nextjs.org/blog/styling-next-with-...
Thanks! I've missed that one. I think I focused only on styled-components and JSS and forgot about Styled JSX.
Thank you for the article, very informative. I spotted a typo. Last work on the Server Side Rendering paragraph -> 'quite quckly'
Thanks!