Quick Nextjs
1 Extensions setup
- Install Es-lint and Prettier vs-code plugin if you havent done so
- In vs-code settings search “default formatter” and set to “Prettier”
- Important vscode trick : highlight codeblock, Ctrl+Shift+P > Emmet: Wrap with abbreviation, enter
div.col
generates<div className="col">..</div>
- Set a keyboard shortcut to this like Shift+Alt+[
- Very useful for positioning JSXElements via Bootstrap’s
<div className="row"
and<div className="col"
2 install
3 Setup bootstrap
import "bootstrap/dist/css/bootstrap.min.css";
import type { AppProps } from "next/app";
import { useEffect } from "react";
export default function App({ Component, pageProps }: AppProps): any {
useEffect(() => {
require("bootstrap/dist/js/bootstrap.bundle.min.js");
, []);
}return <Component {...pageProps} />;
}
4 Setup configs
npx eslint --init
? How would you like to use ESLint? …
To check syntax only
To check syntax and find problems
▸ To check syntax, find problems, and enforce code style
? What type of modules does your project use? …
▸ JavaScript modules (import/export)
CommonJS (require/exports)
None of these
? Which framework does your project use? …
▸ React
Vue.js
None of these
? Does your project use TypeScript? ‣ No / [Yes]
? Where does your code run? … (Press <space> to select, <a> to toggle all, <i> to invert selection)
▸ Browser
Node
? How would you like to define a style for your project? …
▸ Use a popular style guide
Answer questions about your style
? Which style guide do you want to follow? …
▸ Standard: https://github.com/standard/eslint-config-standard-with-typescript
XO: https://github.com/xojs/eslint-config-xo-typescript
? What format do you want your config file to be in? …
JavaScript
YAML
▸ JSON ? Missing packages. Would you like to install them now? ‣ No / [Yes]
npm install --save-dev prettier
npm install --save-dev eslint-config-prettier
npm install --save-dev eslint-plugin-prettier
npm install --save-dev eslint-config-next
4.1 files
- make a typedoc.json file and .env file
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["./pages/*.tsx"],
"out": "docs"
}
ghpageURL=mygithubrepoName #comment the above out if running locally
- modify next.config.js to below so that the relative path works with ghpages
/** @type {import('next').NextConfig} */
const ghpageURL = process.env.ghpageURL ? `/${process.env.ghpageURL}` : undefined;
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
assetPrefix: ghpageURL,
basePath: ghpageURL,
images: {
unoptimized: true
}
}.exports = nextConfig module
5 Setup eslint
- Add this
.eslintignore
file
**/node_modules/*
**/out/*
**/.next/*
**/next-env.d.ts
**/next.config.js
**/docs/*
**/storybook/* **/storybook-static/*
- Add
"react/prop-types": "off", "react/react-in-jsx-scope": "off"
to eslint “rules” - Add
"plugin:@next/next/recommended",
to eslint “extends” - Add
"project": ["tsconfig.json"],
to eslint “parserOptions”s
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"plugin:react/recommended",
"standard-with-typescript",
"prettier", //<-- add this
"plugin:@next/next/recommended" //<-- add this
],
"overrides": [],
"parserOptions": {
"project": ["tsconfig.json"], //<-- add this
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"prettier" //<-- add this
],
"rules": {
"prettier/prettier": ["error"], //<-- addthis
"@typescript-eslint/no-misused-promises": ["error", { "checksVoidReturn": false }], //<-- add this
"react/prop-types": "off", // <-- add this
"react/react-in-jsx-scope": "off", // <-- add this
"react/jsx-props-no-spreading": "off" // <-- add this
}
}
5.1 Run eslint and prettier
npm run lint
In package.json add to scripts "prettier": "prettier --write ."
npm run prettier
- Ctrl+Shift+P > ESLint: Fix all auto-fixable problems
- You have to do this for each page
5.2 Typical lint errors
- PROBLEM: Nextjs page return type not given
- SOLUTION: Add
any
type
- SOLUTION: Add
import { NextPage } from 'next'
function Home (): any { .... export default
6 File structure determines routing
Page filename in nextjs like “landingpage.js” must be lowercase letters
React Component filename in nextjs must be Uppercase like “MyCustomButton”.
Modules will
- .next
- package.json
- package-lock.json
- node_modules
- src
- components
- MyCustomButton
- pages
- [shipID] :: folder => represents a route
- index.tsx => https://mywebsite.com/[shipID]
- [modelID].tsx => https://mywebsite.com/[shipID]/[modelID]
- landingpage.tsx => https://mywebsite.com/landingpage.html
- index.tsx
- _app.tsx :: layout_template
- _document.tsx :: custom layout_template
- api :: folder <IGNORE, it’s for backend>
- [shipID] :: folder => represents a route
- components
- styles
- theme.tsx
7 Next auth
npm install --save next-auth
8 How do you select SSG, PWA, SSR
- Using react hooks means it “may” rerender thus behaving like a PWA
- Using getStaticProp() and never using hooks gives you a static site
Notice how the lines are actually blurred. You can mix and match so the site behaves like an PWA or a static site.
9 Capturing the [carID] and [modelID] as variables
import { useRouter } from 'next/router'
export default function Home() {
const router = useRouter();
const {myrouteINPUT} = router.query;
return(<div>
<h1>hi the text in the url parameter is {myrouteINPUT}
</h1>
</div>)
}
10 ServerSide Render
- getStaticPath prerenders dynamic routes, basically you tell [car].js to use
{toyota, hyundai}
then we prebuildtoyota.html hyundai.html
- getStaticProps fetches data on build when
next build
is called. It only runs on server-side.
The client or window will never see any network call.
This is NOT likewindow.addEventlistener('onload',..)
that fetches data on each client request.
10.1 2 types of SSG
getStaticProps() | getStaticPaths() |
---|---|
calls on build | calls on first request |
10.2 Why SSG can be bad?
SSG at build time and SSG at first request both are affected by stale data.
- If db changes once, SSG at build time is already stale
- If db changes more than once, SSG at first request becomes stale This means if more than one update in db we have to rebuild the ENTIRE site.
10.3 Solution to Stale SSG - ISR
- Solution ISR which basically means the specific static page ONLY will be rebuilt,
- If your clientside code auto refreshes every 10 sec, the data will be relatively current.
11 Self is Not Defined
- When importing react Pixi
Reason for error: Pixi library uses ‘self’ or ‘window’ which is only defined in the client side. NextJS does prerendering always on the serverside meaning it will run Pixi on the serverside where there is no ‘self’ or ‘window’.
Fix:
Use
import dynamic from 'next/dynamic';
Use useEffect because it only runs on client side
12 Exporting as static site
modify package.json for "export": "next export"
npm run build
npm run export
build will create an .next
file that is only useful for servers
export will create an out
file containing static files like index.html
All we need are the out
files
Errors:
- imageOptimisation only works with a server which we dont have. Fis with the experimental turn off image optimization option.
- Paths are by default relative. Nextjs believes
http://server.opnroot.com:5500/
is our root. We need to modify our paths with assetPrefix and basePath so it points tohttp://server.opnroot.com:5500/test1/out
as root.- apparently assetPrefix is js files and css files while basePath is for images and route links
/** @type {import('next').NextConfig} */
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants')
let isProd = process.env.myEnv === "prod";
.exports = (phase, { defaultConfig }) => {
moduleif (phase === PHASE_DEVELOPMENT_SERVER){ isProd = false };
return {
/* config options for all phases except development here */
reactStrictMode: true,
assetPrefix: isProd ? "/MathematicaDocNetwork" : undefined, //static assets are in the /out folder
basePath: isProd ? "/MathematicaDocNetwork" : undefined,
experimental: {
images: {
unoptimized: true,
,
}
}
} }
12.1 Images
Also note that referring to images in *.jsx must be /vercel.svg
, NOT ./vercel.svg
or vercel.svg
.
Example: <Image src={"/vercel.svg"} width={50} height={50}></Image>
12.2 Dynamic Routes DONT work in SSG
- Dynamic routes don’t really work but the build still runs
getStaticPaths()
is used to pregenerate the routes which defeats the whole purpose of “Dynamic” routes.
import Head from 'next/head'
import { useRouter } from 'next/router'
export default function Home(props) {
const {BLEHdata} = props // This is data loaded from getStaticProps
console.log(BLEHdata)
const router = useRouter();
const {myrouteINPUT} = router.query;
//NOTICE `myrouteINPUT` is undefined,
//since exported static site cant capture dynamic routes
return(<div>
<h1>hi
{myrouteINPUT}</h1>
</div>)
}
//this pregenerates the "dynamic route" for [car].js
//REQUIRED FOR ANY DYNAMIC ROUTE that gets exported as static site.
//clearly this isnt really that dynamic since we have to hardcode this.
export async function getStaticPaths(){
return{
paths: [{ params : {car: "1"}}],
fallback: true,
}
}
//This is NOT like just calling window.addEventlistener("onload",...)
//This runs on build time, meaning the browser will NEVER see the GET request to the json server.
export async function getStaticProps(){
const response =await fetch("http://jsonplaceholder.typicode.com/users")
const BLEHdata = await response.json()
console.log(BLEHdata) //this will not output on browser but in terminal
return{
props: {BLEHdata}
} }
13 ISR
interface IServerData {
serverData: any;
}export default function Home({ serverData }: IServerData): any {...}
export async function getStaticProps(): Promise<any> {
const getData = async (
= "https://api.github.com/repos/userJY/someRepo/contents/ooout.json"
url : Promise<any> => {
)// Default options are marked with *
const response = await fetch(url, {
method: "GET", // *GET, POST, PUT, DELETE, etc.
mode: "cors", // no-cors, *cors, same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, *same-origin, omit
headers: {
Authorization:
"token github_pat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
Accept: "application/vnd.github.v3.raw",
"Content-Type": "application/json",
// 'Content-Type': 'application/x-www-form-urlencoded',
,
}redirect: "follow", // manual, *follow, error
referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
;
})return await response.json(); // parses JSON response into native JavaScript objects
;
}
const serverData = await getData();
// console.log("terminal:[size]", BLEHdata); // this will not output on browser but in terminal
return {
props: { serverData },
revalidate: 100,
;
} }