$ cd your-project # jump into the project $ rm -Rf .git # delete the local git folder $ git init # start off your project clean $ npm i # or yarn if you fancy
## Usage
- `npm run dev` to start working locally.
- `npm run build` to build for production.
- `npm start` to run in production.
- `npm run export` to export the site to static html.
## Folder structure
- `./components`
Holds all components (repeating sections / widgets) that are used by `pages` to composite pages.
- `./pages`
Holds all pages (containers), each file should represent one route. They are automatically routed by their filename, but can have a custom route name (see further down below).
- `./pages/_document.js`
The underlying html for every page render. Try to stick to the pages components if possible, but use it for elegant side-wide stuff and if you need to integrate something that otherwise can't be done. See [Custom Document](https://github.com/zeit/next.js#custom-document) for more info on customization.
- `./pages/_errors.js`
404 or 500 errors from both client- and server side are handled by this component. We again have a sane default here, which frankly can be customized as needed.
- `./services`
Every piece of JavaScript that does not export a Component should be housed here. Classic candidates are Singletons that encapsulate functionality like SDKs.
- `./static`
Holds all static files (fonts / images / videos etc.), can also be used to transfer things like `robots.txt` or `favicon.ico`.
The `/pages` convention is pretty nifty and forces you to do bright decisions and poses a simple way for a new developer that works on the project to understand which comp is rendered from which route
In general – think the React way, for your convenience we also placed a `_global.js` file in the `/pages` directory which you probably want to use as the top-level wrapper of your pages. This file allows you to have styles like **fonts** or a css reset available globally. You can use it as a Layout file to have general markup as well. Or you separate it into its own file – in the end up to you.
## Images / Static Files
Sometimes back to the roots is nice. Simply put your stuff into `/static` and reference it as usual, but prefix the URLs with `/static/<filename>`. For **fonts** we recommend to simply put them in the `/static` folder and put font-face declarations into a Wrapper Component like `_global.js`.
## CSS/SASS Styles
We use SASS in combination with CSS modules. This means that every classname you use in your .scss files will be turned into a unique hash. You can reference these hashes as entries in the style object that you import. This has the advantage of guaranteed unique classnames so scoping is not an issue anymore. Here is an example:
```js
// next.config.js
const withSass = require('@zeit/next-sass')
module.exports = withSass({
cssModules: true
})
Create a Sass file styles.scss
$font-size: 50px;
.example {
font-size: $font-size;
}
Create a page file pages/index.js
import css from "../styles.scss"
export default () => <div className={css.example}>Hello World!</div>
Next runs mostly on convention over configuration, we'll highlight a few important pieces here, but for all your questions, simply head to their zeit/next repository to get all your questions answered.
Most importantly, you can safely write next-generation code and expect it to be transpiled (eg. await/async
).
The project introduces a linting setup that automatically lints your staged files when you try to commit. Apart from that you can run npm run lint
as well to lint the whole project (not just staged files), but without committing. Additionally, there is the npm run lint:withfix
command that shows all errors and tries to fix minor warnings automatically (eg. it switches quotes, spaces etc.)
You will probably run into lots of issues the longer you work on a commit, so it is recommended to setup linting in your code editor as well to mitigate issues right as they occur. This is not necessary, but helps. Since the package.json
has the right rules in it, your local Eslint Editor Plugin will use the same rules. Here are a couple of handy links: Atom Plugin, Sublime Plugin, WebStorm, vim, Visual Studio Extension.
As soon as your component has more than 300 lines, you should split it up into child components, unless you can express a valid reson why you'd rather not want to.
We use a custom webpack loader so you can import the strings of .glsl
-files. You can then import other shader files as if it were SASS and you have the great glsl syntax highlighting that you love.
// ./include.glsl
foo
// ./shader.glsl
@import ./include
goo
// ./page.js
import shader from './shader.glsl';
/*
// the "shader"-var is now this string:
foo
goo
*/
Out of the box next
has the convention of automatically gathering routes by .js
-files in the /pages
directory. As soon as you want parameterized urls, this alone is not really that beneficial anymore. Instead we have a routes.js
file in the root that routes URLs to pages within the /pages
directory. This file is used both on the server and the client.
Note: If you have no dynamic urls, simply don't add any routes in the routes.js
file. The regular routes from the /pages
filenames continue to work.
// routes.js
const nextRoutes = require('next-routes')
const routes = module.exports = nextRoutes()
routes.add('blog', '/blog/:slug')
routes.add('about', '/about-us/:foo(bar|baz)', 'index')
API: routes.add(name, pattern, page = name)
name
- The route namepattern
- Express-style route pattern (uses path-to-regexp)page
- Page inside ./pages to be rendered (defaults to name)
The page component receives the matched URL parameters merged into query
export default class Blog extends React.Component {
static async getInitialProps ({query}) {
// query.slug
}
render () {
// this.props.url.query.slug
}
}
Since this is custom, we can't use the default Link
and Router
interfaces from next
. Thin wrappers around Link
and Router
add support for generated URLs based on route name and parameters. Just import them from your routes
file:
// pages/index.js
import {Link} from '../routes'
export default () => (
<div>
<div>Welcome to next.js!</div>
<Link route='blog' params={{slug: 'hello-world'}}>
<a>Hello world</a>
</Link>
</div>
)
API: <Link route="name" params={params}>...</Link>
route
- Name of a routeparams
- Optional parameters for the route URL
It generates the URL and passes href
and as
props to next/link
. Other props like prefetch
will work as well.
// pages/blog.js
import React from 'react'
import {Router} from '../routes'
export default class extends React.Component {
handleClick () {
Router.pushRoute('about', {foo: 'bar'})
}
render () {
return (
<div>
<div>{this.props.url.query.slug}</div>
<button onClick={this.handleClick}>
Home
</button>
</div>
)
}
}
API:
Router.pushRoute(name, params, options)
Router.replaceRoute(name, params, options)
name
- Name of a routeparams
- Optional parameters for the route URLoptions
- Optional options
It generates the URL and passes href
and as
parameters to next/router
.
You can optionally provide custom Link
and Router
objects, for example:
// routes.js
const nextRoutes = require('next-routes')
const Link = require('./my/link')
const Router = require('./my/router')
const routes = module.exports = nextRoutes({Link, Router})
For changing things that happen in the <head>
part of the page, make use of the next/head
helper, see this example as it is pretty self-explanatory:
import Head from 'next/head'
export default () => (
<div>
<Head>
<title>My page title</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<p>Hello world!</p>
</div>
)
Note: The contents of get cleared upon unmounting the component, so make sure each page completely defines what it needs in , without making assumptions about what other pages added
Our stack uses a headless CMS, so chance are very high that you want to fetch data before rendering a component (server-side-rendering) or navigating to a new page on the client. next
uses a static async function called getInitialProps()
that should be used exactly for that. Do yourself a favor and use the async/await
syntax for added beauty.
import React from 'react';
export default class extends React.Component {
static async getInitialProps() {
const res = await fetch('https://api.example.com/article/1');
const json = await res.json();
return { headline: json.headline }
}
render() {
return (
<div>
Hello World {this.props.headline}
</div>
);
}
}
getInitialProps
receives a context object with the following properties:
pathname
- path section of URLquery
- query string section of URL parsed as an objectasPath
- the actual url pathreq
- 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
As you will probably work off a local
, a staging
and a production
environment, you can make use of the env.config.js
file. It exports one object with the respective properties you define depending on your current environment. Simply import it in your scripts, we made sure the environment is properly read both on the server and the client.
Do not put sensitive information like access data here, this file might be bundled to the client! If you want to store access data for eg. database access, you will always only need it on the server, so you should either read it from environment variables or store it in a not-checked-into-git-file that you require in node and note its existence in your project readme.
This way you can easily build against different environments and eg. insert a different API Url or Google Analytics snippet to staging than production.
Make sure you fill out all fields you use for all environments you use, there is no checking from the boilerplate if you do so to keep things simple and flexible.
In our boilerplate next
is per default integrated into a express
server. That means you can easily integrate middleware or a full database-fetching API into the server. Simply open up server.js
in the root and edit to your likings. There is a sample line for an API route in there for your convenience.
The export feature can still be considered quite experimental with an API that will most likely change. Yet it is powerful and allows you to pre-render all routes into static html files, which makes the site eligible for static hosting on S3, which as you know is ugly-ass-cheap.
Running npm run export
runs the build process first and then exports the site into the /dist
subdirectory. This directoy is what you put up on your static hosting services as you are used to.
In the project root, there is a next.config.js
, which has a exportPathMap
export. It is a function that returns routes as key-value-pairs. At this moment, you have to manually mention all routes you want to be available. In future releases this process will be unified into one routing API, so you don't have three places to put routes into.
exports.exportPathMap = () => ({
"/": { page: "/" },
"/about": { page: "/about" },
"/product/plant": { page: "/product", query: { slug: "plant" } },
"/product/tree": { page: "/product", query: { slug: "tree" } },
});
Note the following caveats:
With npm run export
, we build an HTML version of your app. At that time, we will automatically execute the getInitialProps
functions of your pages and store the results.
This means you can only use pathname
, query
and asPath
fields of the context
object passed to getInitialProps
. You cannot use req
or res
fields, because those require a server runtime.
Basically, you will not be able to render content dynamic in the server as we prebuilt HTML files. If you need that, you need run your app with npm start
.
Sample nginx reverse proxy config
location / {
# default port, could be changed if you use next with custom server
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# if you have try_files like this, remove it from our block
# otherwise next app will not work properly
# try_files $uri $uri/ =404;
}
Sample apache reverse proxy config
``` ServerName domain.com ProxyRequests Off ProxyPreserveHost On ProxyVia Full Require all granted ProxyPass / http://localhost:8080/ connectiontimeout=5 timeout=30 ```Sample pm2 line to start the server
NODE_ENV=staging WILD_ENV=staging PORT=8080 pm2 start npm --name "staging" -- start