@_nati

SSR

universal

isomorphic

Setup

> mkdir my-next-app
> cd my-next-app
> yarn init -y
> yarn add react react-dom next@beta
> mkdir pages
{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

package.json

Pages

pages/index.js

export default () => (
  <div>Welcome to next.js!</div>
)
// pages/index.js

export default () => (
  <div>
    Hello world
    <p>scoped!</p>
    <style jsx>{`
      p {
        color: blue;
      }
      div {
        background: red;
      }
      @media (max-width: 600px) {
        div {
          background: blue;
        }
      }
    `}</style>
    <style global jsx>{`
      body {
        background: black;
      }
    `}</style>
  </div>
)

Populating <head>

import Head from 'next/head'

export default () => (
  <div>
    <Head>
      <title>My page title</title>
    </Head>
    <p>Hello world!</p>
  </div>
)

Simple Fetching data

import fetch from 'isomorphic-unfetch'

const Page = ({ stars }) => (
  <div>Next stars: {stars}</div>
)

Page.getInitialProps = async ({ req }) => {
  const res = await fetch('https://api.github.com/repos/zeit/next.js')
  const json = await res.json()
  return { stars: json.stargazers_count }
}

export default Page

getInitialProps

receives a context object with the following properties:

  • pathname - path section of URL
  • query - query string section of URL parsed as an object
  • asPath - the actual url path
  • req - HTTP request object (server only)
  • res - HTTP response object (server only)
  • jsonPageRes - Fetch Response¬†object (client only)
  • err - Error object if any error is encountered during the rendering
const Page = ({pathname, query}) => (
  <div>
    <pre>{JSON.stringify(pathname)}</pre>
    <pre>{JSON.stringify(query)}</pre>
  </div>
)

Page.getInitialProps = async ({pathname, query}) => {
  return {
    pathname,
    query
  }
}

export default Page

getInitialProps

Routing

next/link

import Link from 'next/link'
export default () => (
  <Link href="/about"><a>About</a></Link>
)

next/router

import Router from 'next/router'

export default () => (
  <span onClick={() => Router.push('/about')}>here</span>
)

Dynamic Pages

// pages/post.js

export default (props) => (
  <div>
    <h1>{props.url.query.title}</h1>
  </div>
)

http://localhost:3000/post?title=Hello%20Next.js

http://localhost:3000/post?title=Hello%20Next.js

http://localhost:3000/p/hello-nextjs

vs

Clean URLs with Route Masking

// pages/index.js

export default () => (
  <Link as={'/p/hello-nextjs'} href={'/post?title=Hello%20Next.js'}>
    <a>Hello Next.js</a>
  </Link>
)

Server Side Support for Clean URLs

// server.js

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

  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)
})
// package.json

"scripts": {
  "start": "NODE_ENV=production node server.js"
}
// next.config.js

module.exports = {
  exportPathMap: function () {
    return {
      "/": { page: "/" },
      "/p/hello-nextjs": { page: "/post", query: { title: "hello-nextjs" } }
    }
  }
}

Static HTML export

// package.json

"scripts": {
  "build": "next build && next export"
}
// package.json

"scripts": {
  "deploy": "next build && next export && node_modules/gh-pages/bin/gh-pages -d out"
}
> yarn add gh-pages
> yarn deploy

Deploy Static Web to Github Pages

Next.js | React BKK 2.0

By Nati Namvong

Next.js | React BKK 2.0

  • 930