Next is a React framework for server-rendered React applications.
isomorphic apps (code that runs in the browser or in node)
note from the getting started guide of Next.js
mkdir hello-next
cd hello-next
npm init next-app .
Pick a template » - Use arrow-keys. Return to submit.
√ Pick a template » Default starter app
Creating a new Next.js app in C:\code\hello-next.
Installing react, react-dom, and next using yarn...
The only special directory is the pages directory.
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
npm run dev
Next.js is all about pages. We can create a page by exporting a React component, and putting that component inside the pages directory. Then it will have a fixed URL based on the file name.
The only special directory is the pages directory.
A page:
import Link from 'next/link'
import Header from '../components/Header'
export default function Index() {
return (
<div>
<Header />
<p>Hello Next.js</p>
</div>
)
}
Use cases for shared components:
A component directory can be named anything (by convention components), the only special directory is the pages directory. You can even create the Component inside the pages directory if you need a direct URL to your component.
import Link from 'next/link'
const linkStyle = {
marginRight: 15
}
const Header = () => (
<div>
<Link href="/">
<a style={linkStyle}>Home</a>
</Link>
<Link href="/about">
<a style={linkStyle}>About</a>
</Link>
</div>
)
export default Header
see React - Composite Component (Layout) / Container Component
Navigation / Routing
// This is the Link API
import Link from 'next/link'
const Index = () => (
<div>
<Link href="/about">
<a>About Page</a>
</Link>
<p>Hello Next.js</p>
</div>
)
export default Index
Example:
const PostLink = props => (
<li>
<Link as={`/p/${props.id}`} href={`/post?title=${props.title}`}>
<a>{props.title}</a>
</Link>
</li>
)
SSR with custom-server-and-routing
You can use any Node.js Web server with this API.
For instance, with express
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app
.prepare()
.then(() => {
const server = express()
// Custom route
server.get('/p/:id', (req, res) => {
const actualPage = '/post'
const queryParams = { title: req.params.id }
app.render(req, res, actualPage, queryParams)
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, err => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
.catch(ex => {
console.error(ex.stack)
process.exit(1)
})
npm install --save express
npm install --save-dev cross-env
{
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "cross-env NODE_ENV=production node server.js"
}
}
If we can only see the message on the browser console. That's because we navigated to the post page via the client side.
When we click on a link wrapped with the Next.js <Link> component, the page transition takes place in the browser, without making a request to the server.
If you just visit a post page directly by typing the URL in the browser address (eg:- http://localhost:3000/p/975) you will see the console message printed on the server but not in the client.
dynamic pages using query strings client side
We're injecting the router property (included the query string) into a component by calling withRouter on it
import { withRouter } from 'next/router'
import Layout from '../components/MyLayout.js'
// Query prop of router is injected into props thanks to withRouter
const Content = withRouter(props => (
<div>
<h1>{props.router.query.title}</h1>
<p>This is the blog post content.</p>
</div>
))
const Page = withRouter(props => (
<Layout>
<Content />
</Layout>
))
export default Page
Browser - Fetching Resources (Request/Response) in Next
Next.js comes with an async function called getInitialProps to fetch data for pages.
By using the getInitialProps static async function, we can fetch data and send them as props to our page.
Example: passing data into our Index page as the shows prop.
# a web api fetch library wrapper that works on the server and client side
npm install --save isomorphic-unfetch
import Layout from '../components/MyLayout.js'
import Link from 'next/link'
import fetch from 'isomorphic-unfetch'
const Index = (props) => (
<Layout>
<h1>Batman TV Shows</h1>
<ul>
{props.shows.map(show => (
<li key={show.id}>
<Link as={`/p/${show.id}`} href={`/post?id=${show.id}`}>
<a>{show.name}</a>
</Link>
</li>
))}
</ul>
</Layout>
)
Index.getInitialProps = async function() {
const res = await fetch('https://api.tvmaze.com/search/shows?q=batman')
const data = await res.json()
// If the message is printed on the server, it's a SSR otherwise it's a client rendering
console.log(`Show data fetched. Count: ${data.length}`)
return {
// passing data into our page as the 'shows' prop.
shows: data.map(entry => entry.show)
}
}
export default Index
Next has a built-in Css In Js framework called styled-jsx
Style are defined inside the <style jsx></node> element with a Js template string
Styled jsx works as a babel plugin (all necessary prefixing and CSS validation is done inside the plugin). It will parse all of the CSS and apply it in the build process. (The styles get applied without any additional runtime overhead)
It also supports having constraints inside styled-jsx. In the future, you will be able to use any dynamic variable inside styled-jsx. That is why CSS needs to go inside of a template string. ({})
Scope of style is:
Next.js support also other solution next.js css-in-js
import Layout from '../components/MyLayout.js'
import Link from 'next/link'
function getPosts() {
return [
{id: 'hello-nextjs', title: 'Hello Next.js'},
{id: 'learn-nextjs', title: 'Learn Next.js is awesome'},
{id: 'deploy-nextjs', title: 'Deploy apps with ZEIT'}
]
}
export default function Blog() {
return (
<Layout>
<h1>My Blog</h1>
<ul>
{getPosts().map(post => (
<li key={post.id}>
<Link as={`/p/${post.id}`} href={`/post?title=${post.title}`}>
<a>{post.title}</a>
</Link>
</li>
))}
</ul>
<style jsx>{`
h1,
a {
font-family: 'Arial';
}
ul {
padding: 0;
}
li {
list-style: none;
margin: 5px 0;
}
a {
text-decoration: none;
color: blue;
}
a:hover {
opacity: 0.6;
}
`}</style>
</Layout>
)
}
<style jsx global></script>
Example:
npm install --save react-markdown
import Layout from '../components/MyLayout.js'
import { withRouter } from 'next/router'
import Markdown from 'react-markdown'
export default withRouter(props => (
<Layout>
<h1>{props.router.query.title}</h1>
<div className="markdown">
<Markdown
source={`
This is our blog post.
Yes. We can have a [link](/link).
And we can have a title as well.
### This is a title
And here's the content.
`}
/>
</div>
<style jsx global>{`
.markdown {
font-family: 'Arial';
}
.markdown a {
text-decoration: none;
color: blue;
}
.markdown a:hover {
opacity: 0.6;
}
.markdown h3 {
margin: 0;
padding: 0;
text-transform: uppercase;
}
`}</style>
</Layout>
))
Next.js 3.0 comes with a feature that allows you to export an app into a set of HTML pages. See Export into a Static HTML App
Next.js does automatic code splitting and it is based on the pages in your app. For example, if one of your modules is used at-least in half of your pages, then it moves into the main JavaScript bundle. If not, that module stays inside the page's bundle.
You can deploy a Next.js app to anywhere you can run Node.js.
Build produce an optimized set of code for production
"scripts": {
"build": "next build"
"start": "next start"
"startonlinuxport": "next start -p $PORT"
"startonwindowsport": "next start -p %PORT%"
}
npm run build
Creating an optimized production build ...
Compiled successfully.
┌ /
├ /_app
├ /_document
├ /_error
├ /about
└ /post
npm run start
:: Install cross-env globally
npm install cross-env -g
:: Run on port 8000
cross-env PORT=8000 npm run startonwindowsport
:: Run on port 9000
cross-env PORT=9000 npm run startonwindowsport
The now platform has special builder for next application called @now/next. The builder will convert the next pages into a series of individual lambdas and serves them statically. More … see Create a Next.js Application and Deploy with Now guide
module.exports = {
target: 'serverless'
}
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"name": "<name-for-your-project>"
}
npm install -g now
now