React Framework - Next.js

About

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

Features

Steps

Project Init

  • Create the repository
mkdir hello-next
cd hello-next
  • init - Create a new app with create-next-app (yarn under the hood) in the current directory
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.

Next Package.json Script

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

Start Dev Server

npm run dev

Page

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:

  • importing a custom component
  • and exporting an other React component (ie export default
import Link from 'next/link'

import Header from '../components/Header'

export default function Index() {
    return (
        <div>
            <Header />
            <p>Hello Next.js</p>
        </div>
    )
}

Component

Use cases for shared components:

  • As common components.
  • As Layouts.

Common Component

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.

A React 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

Layout

see React - Composite Component (Layout) / Container Component

Navigation / Routing

  • client-side navigation: Link API, which is exported via next/link (handle the location.history of the browser)
  • next/link is a higher order component which only accepts the “href” and some similar props.
  • Link Works With Anything inside. Just like an anchor, a button, you can place any React components or even a div
  • The only requirement for components placed inside a Link is they should accept an onClick prop
// 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

Route masking

  • The as prop is the URL in the browser bar (logical)
  • The href prop is the URL the app is seeing (physical)

Example:

const PostLink = props => (
  <li>
    <Link as={`/p/${props.id}`} href={`/post?title=${props.title}`}>
      <a>{props.title}</a>
    </Link>
  </li>
)

Processing localization

SSR

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)
  })
  • Install
npm install --save express
npm install --save-dev cross-env
  • Package.json
{
  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "cross-env NODE_ENV=production node server.js"
  }
}

SSR vs Client Side

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.

Query String

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

Fetch data

Browser - Fetching Resources (Request/Response) in Next

Next.js comes with an async function called getInitialProps to fetch data for pages.

  • It fetch data via a remote data source and pass it as props
  • We can write our getInitialProps to work on both server and the client.

By using the getInitialProps static async function, we can fetch data and send them as props to our page.

Fetch Next Doc

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

Styling

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

Local

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>
    )
}

Global

<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>
))

HTML page generation

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

Lazy loading

Code splitting

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.

https://nextjs.org/learn/excel/lazy-loading-modules

React Component

https://nextjs.org/learn/excel/lazy-loading-components

Deploy

You can deploy a Next.js app to anywhere you can run Node.js.

Locally

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

  • Once on the default 300 port
npm run start
  • on windows, two instances with two different ports
:: 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

now

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
  • run the “now” command from your terminal inside your app's root directory.
now

More Serverless Hosting - Now

Support / Community

App

Documentation / Reference

Task Runner