diff --git a/.alexignore b/.alexignore new file mode 100644 index 0000000000000..1bf6581c26b1e --- /dev/null +++ b/.alexignore @@ -0,0 +1,2 @@ +CODE_OF_CONDUCT.md +examples/ diff --git a/.alexrc b/.alexrc new file mode 100644 index 0000000000000..157d1da8cca53 --- /dev/null +++ b/.alexrc @@ -0,0 +1,21 @@ +{ + "allow": [ + "attacks", + "color", + "dead", + "execute", + "executed", + "executes", + "execution", + "executions", + "failed", + "failure", + "failures", + "fire", + "fires", + "hook", + "hooks", + "host-hostess", + "invalid" + ] +} diff --git a/.eslintignore b/.eslintignore index 016351f511ae9..22d834344e383 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,10 +2,27 @@ node_modules **/.next/** **/_next/** **/dist/** +e2e-tests/** +examples/with-eslint/** examples/with-typescript-eslint-jest/** examples/with-kea/** +examples/with-custom-babel-config/** +examples/with-flow/** +examples/with-jest/** +examples/with-mobx-state-tree/** +examples/with-mobx/** +packages/next/bundles/webpack/packages/*.runtime.js packages/next/compiled/**/* packages/react-refresh-utils/**/*.js packages/react-dev-overlay/lib/** **/__tmp__/** -.github/actions/next-stats-action/.work \ No newline at end of file +.github/actions/next-stats-action/.work +packages/next-codemod/transforms/__testfixtures__/**/* +packages/next-codemod/transforms/__tests__/**/* +packages/next-codemod/**/*.js +packages/next-codemod/**/*.d.ts +packages/next-env/**/*.d.ts +packages/create-next-app/templates/** +test/integration/eslint/** +test-timings.json +packages/next/build/swc/tests/fixture/** diff --git a/.eslintrc.json b/.eslintrc.json index a3de6444e0e62..1cee5507a5901 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,6 @@ { "root": true, - "parser": "babel-eslint", + "parser": "@babel/eslint-parser", "plugins": ["react", "react-hooks", "jest", "import"], "env": { "browser": true, @@ -9,16 +9,24 @@ "node": true }, "parserOptions": { - "ecmaVersion": 2018, + "requireConfigFile": false, "sourceType": "module", "ecmaFeatures": { "jsx": true + }, + "babelOptions": { + "presets": ["@babel/preset-env", "@babel/preset-react"], + "caller": { + // Eslint supports top level await when a parser for it is included. We enable the parser by default for Babel. + "supportsTopLevelAwait": true + } } }, "settings": { "react": { "version": "detect" - } + }, + "import/internal-regex": "^next/" }, "overrides": [ { @@ -26,7 +34,10 @@ "extends": ["plugin:jest/recommended"], "rules": { "jest/expect-expect": "off", - "jest/no-disabled-tests": "off" + "jest/no-disabled-tests": "off", + "jest/no-conditional-expect": "off", + "jest/valid-title": "off", + "jest/no-interpolation-in-snapshots": "off" } }, { "files": ["**/__tests__/**"], "env": { "jest": true } }, @@ -110,6 +121,29 @@ } ] } + }, + { + "files": ["packages/**"], + "rules": { + "no-shadow": ["warn", { "builtinGlobals": false }], + "import/no-extraneous-dependencies": [ + "error", + { "devDependencies": false } + ] + } + }, + { + "files": ["packages/**/*.tsx", "packages/**/*.ts"], + "rules": { + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "all", + "argsIgnorePattern": "^_", + "ignoreRestSiblings": true + } + ] + } } ], "rules": { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index af2e4246fca4f..5b64af494dbb6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,9 +1,6 @@ # Learn how to add code owners here: # https://help.github.com/en/articles/about-code-owners -* @Timer -/packages/ @timneutkens @Timer @ijjk @lfades -/examples/ @lfades @Timer -/test/ @timneutkens @Timer @ijjk @lfades -/bench/ @timneutkens @Timer -/errors/ @Timer +* @timneutkens @ijjk @shuding @styfle @huozhi @padmaia +/docs/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @leerob @lfades +/examples/ @timneutkens @ijjk @shuding @leerob @lfades diff --git a/.github/ISSUE_TEMPLATE/1.Bug_report.md b/.github/ISSUE_TEMPLATE/1.Bug_report.md deleted file mode 100644 index 3204d20475f45..0000000000000 --- a/.github/ISSUE_TEMPLATE/1.Bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Bug report -about: Create a bug report for the Next.js core / examples ---- - -# Bug report - -## Describe the bug - -A clear and concise description of what the bug is. - -## To Reproduce - -Steps to reproduce the behavior, please provide code snippets or a repository: - -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -## Expected behavior - -A clear and concise description of what you expected to happen. - -## Screenshots - -If applicable, add screenshots to help explain your problem. - -## System information - -- OS: [e.g. macOS, Windows] -- Browser (if applies) [e.g. chrome, safari] -- Version of Next.js: [e.g. 6.0.2] -- Version of Node.js: [e.g. 10.10.0] - -## Additional context - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml new file mode 100644 index 0000000000000..440f9434bf332 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -0,0 +1,73 @@ +name: Bug Report +description: Create a bug report for the Next.js core +labels: 'template: bug' +body: + - type: markdown + attributes: + value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible. + - type: markdown + attributes: + value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions "Help" section. + - type: markdown + attributes: + value: 'Please first verify if your issue exists in the Next.js canary release line: `npm install next@canary`.' + - type: markdown + attributes: + value: 'next@canary is the beta version of Next.js. It includes all features and fixes that are pending to land on the stable release line.' + - type: input + attributes: + label: What version of Next.js are you using? + description: 'For example: 10.0.1' + validations: + required: true + - type: input + attributes: + label: What version of Node.js are you using? + description: 'For example: 12.0.0' + validations: + required: true + - type: input + attributes: + label: What browser are you using? + description: 'For example: Chrome, Safari' + validations: + required: true + - type: input + attributes: + label: What operating system are you using? + description: 'For example: macOS, Windows' + validations: + required: true + - type: input + attributes: + label: How are you deploying your application? + description: 'For example: next start, next export, Vercel, Other platform' + validations: + required: true + - type: textarea + attributes: + label: Describe the Bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior, please provide a clear code snippets that always reproduces the issue or a GitHub repository. Screenshots can be provided in the issue body below. + validations: + required: true + - type: markdown + attributes: + value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear. + - type: markdown + attributes: + value: Contributors should be able to follow the steps provided in order to reproduce the bug. + - type: markdown + attributes: + value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance! diff --git a/.github/ISSUE_TEMPLATE/2.Feature_request.md b/.github/ISSUE_TEMPLATE/2.Feature_request.md deleted file mode 100644 index c3cfe03750198..0000000000000 --- a/.github/ISSUE_TEMPLATE/2.Feature_request.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Feature request -about: Create a feature request for the Next.js core ---- - -# Feature request - -## Is your feature request related to a problem? Please describe. - -A clear and concise description of what you want and what your use case is. - -## Describe the solution you'd like - -A clear and concise description of what you want to happen. - -## Describe alternatives you've considered - -A clear and concise description of any alternative solutions or features you've considered. - -## Additional context - -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/2.example_bug_report.yml b/.github/ISSUE_TEMPLATE/2.example_bug_report.yml new file mode 100644 index 0000000000000..8535a441bf4a7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2.example_bug_report.yml @@ -0,0 +1,73 @@ +name: Example Bug Report +description: Create a bug report for the examples +labels: 'type: example,template: bug' +body: + - type: markdown + attributes: + value: Thanks for taking the time to file a examples bug report! Please fill out this form as completely as possible. + - type: markdown + attributes: + value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions "Help" section. + - type: input + attributes: + label: What example does this report relate to? + description: 'For example: with-styled-components' + validations: + required: true + - type: input + attributes: + label: What version of Next.js are you using? + description: 'For example: 10.0.1' + validations: + required: true + - type: input + attributes: + label: What version of Node.js are you using? + description: 'For example: 12.0.0' + validations: + required: true + - type: input + attributes: + label: What browser are you using? + description: 'For example: Chrome, Safari' + validations: + required: true + - type: input + attributes: + label: What operating system are you using? + description: 'For example: macOS, Windows' + validations: + required: true + - type: input + attributes: + label: How are you deploying your application? + description: 'For example: next start, next export, Vercel, Other platform' + validations: + required: true + - type: textarea + attributes: + label: Describe the Bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior, please provide a clear code snippets that always reproduces the issue or a GitHub repository. Screenshots can be provided in the issue body below. + validations: + required: true + - type: markdown + attributes: + value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear. + - type: markdown + attributes: + value: Contributors should be able to follow the steps provided in order to reproduce the bug. + - type: markdown + attributes: + value: Thanks in advance! diff --git a/.github/ISSUE_TEMPLATE/3.feature_request.yml b/.github/ISSUE_TEMPLATE/3.feature_request.yml new file mode 100644 index 0000000000000..2655aff44d149 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3.feature_request.yml @@ -0,0 +1,28 @@ +name: Feature Request +description: Create a feature request for the Next.js core +labels: 'template: story' +body: + - type: markdown + attributes: + value: Thanks for taking the time to file a feature request! Please fill out this form as completely as possible. + - type: markdown + attributes: + value: 'Feature requests will be converted to the GitHub Discussions "Ideas" section.' + - type: textarea + attributes: + label: Describe the feature you'd like to request + description: A clear and concise description of what you want and what your use case is. + validations: + required: true + - type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true diff --git a/.github/actions/next-stats-action/Dockerfile b/.github/actions/next-stats-action/Dockerfile index bf86727921a9b..f533f918d9d27 100644 --- a/.github/actions/next-stats-action/Dockerfile +++ b/.github/actions/next-stats-action/Dockerfile @@ -1,8 +1,8 @@ -FROM node:10-buster +FROM node:14-buster LABEL com.github.actions.name="Next.js PR Stats" LABEL com.github.actions.description="Compares stats of a PR with the main branch" -LABEL repository="https://github.com/zeit/next-stats-action" +LABEL repository="https://github.com/vercel/next-stats-action" COPY . /next-stats diff --git a/.github/actions/next-stats-action/package.json b/.github/actions/next-stats-action/package.json index 65586812c447a..a9fb8c4e081e7 100644 --- a/.github/actions/next-stats-action/package.json +++ b/.github/actions/next-stats-action/package.json @@ -13,6 +13,7 @@ "node-fetch": "^2.6.0", "prettier": "^1.18.2", "pretty-bytes": "^5.3.0", - "pretty-ms": "^5.0.0" + "pretty-ms": "^5.0.0", + "semver": "7.3.4" } } diff --git a/.github/actions/next-stats-action/src/add-comment.js b/.github/actions/next-stats-action/src/add-comment.js index 1dc37eee62d2f..2f2d9afc15785 100644 --- a/.github/actions/next-stats-action/src/add-comment.js +++ b/.github/actions/next-stats-action/src/add-comment.js @@ -1,5 +1,5 @@ const path = require('path') -const fs = require('fs-extra') +const fs = require('fs').promises const fetch = require('node-fetch') const prettyMs = require('pretty-ms') const logger = require('./util/logger') @@ -83,7 +83,7 @@ module.exports = async function addComment( else if (!isGzipItem && !groupKey.match(gzipIgnoreRegex)) return if ( - itemKey !== 'buildDuration' || + !itemKey.startsWith('buildDuration') || (isBenchmark && itemKey.match(/req\/sec/)) ) { if (typeof mainItemVal === 'number') mainRepoTotal += mainItemVal @@ -101,7 +101,15 @@ module.exports = async function addComment( // check if there is still a change after rounding if (change !== 0) { const absChange = Math.abs(change) - change = `${change < 0 ? '-' : '⚠️ +'}${ + const warnIfNegative = isBenchmark && itemKey.match(/req\/sec/) + const warn = warnIfNegative + ? change < 0 + ? '⚠️ ' + : '' + : change > 0 + ? '⚠️ ' + : '' + change = `${warn}${change < 0 ? '-' : '+'}${ useRawValue ? absChange : prettify(absChange, prettyType) }` } diff --git a/.github/actions/next-stats-action/src/constants.js b/.github/actions/next-stats-action/src/constants.js index 2b31994f5dca0..ba3ec39b2ffd5 100644 --- a/.github/actions/next-stats-action/src/constants.js +++ b/.github/actions/next-stats-action/src/constants.js @@ -8,6 +8,9 @@ const mainRepoDir = path.join(workDir, mainRepoName) const diffRepoDir = path.join(workDir, diffRepoName) const statsAppDir = path.join(workDir, 'stats-app') const diffingDir = path.join(workDir, 'diff') +const yarnEnvValues = { + YARN_CACHE_FOLDER: path.join(workDir, 'yarn-cache'), +} const allowedConfigLocations = [ './', '.stats-app', @@ -24,5 +27,6 @@ module.exports = { mainRepoDir, diffRepoDir, statsAppDir, + yarnEnvValues, allowedConfigLocations, } diff --git a/.github/actions/next-stats-action/src/index.js b/.github/actions/next-stats-action/src/index.js index add5ab4b52561..64b1b1c18e05d 100644 --- a/.github/actions/next-stats-action/src/index.js +++ b/.github/actions/next-stats-action/src/index.js @@ -1,3 +1,5 @@ +const path = require('path') +const fs = require('fs-extra') const exec = require('./util/exec') const logger = require('./util/logger') const runConfigs = require('./run') @@ -25,6 +27,13 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { ;(async () => { try { + if (await fs.pathExists(path.join(__dirname, '../SKIP_NEXT_STATS.txt'))) { + console.log( + 'SKIP_NEXT_STATS.txt file present, exiting stats generation..' + ) + process.exit(0) + } + const { stdout: gitName } = await exec( 'git config user.name && git config user.email' ) @@ -46,6 +55,12 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { ) } + if (actionInfo.isLocal) { + // make sure to use local repo location instead of the + // one provided in statsConfig + statsConfig.mainRepo = actionInfo.prRepo + } + // clone main repository/ref if (!actionInfo.skipClone) { await cloneRepo(statsConfig.mainRepo, mainRepoDir) @@ -59,6 +74,7 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { logger('Release detected, resetting mainRepo to last stable tag') const lastStableTag = await getLastStable(mainRepoDir, actionInfo.prRef) if (!lastStableTag) throw new Error('failed to get last stable tag') + console.log('using latestStable', lastStableTag) await checkoutRef(lastStableTag, mainRepoDir) /* eslint-disable-next-line */ @@ -86,13 +102,17 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { logger(`Running initial build for ${dir}`) if (!actionInfo.skipClone) { let buildCommand = `cd ${dir}${ - !statsConfig.skipInitialInstall ? ' && yarn install' : '' + !statsConfig.skipInitialInstall + ? ' && yarn install --network-timeout 1000000' + : '' }` if (statsConfig.initialBuildCommand) { buildCommand += ` && ${statsConfig.initialBuildCommand}` } - await exec(buildCommand) + // allow 5 minutes node_modules install + building all packages + // in case of noisy environment slowing down initial repo build + await exec(buildCommand, false, { timeout: 5 * 60 * 1000 }) } logger(`Linking packages in ${dir}`) diff --git a/.github/actions/next-stats-action/src/prepare/action-info.js b/.github/actions/next-stats-action/src/prepare/action-info.js index 427fee1fd7745..fff1ffc955cb3 100644 --- a/.github/actions/next-stats-action/src/prepare/action-info.js +++ b/.github/actions/next-stats-action/src/prepare/action-info.js @@ -56,7 +56,9 @@ module.exports = function actionInfo() { isLocal: LOCAL_STATS, commitId: null, issueId: ISSUE_ID, - isRelease: releaseTypes.has(GITHUB_ACTION), + isRelease: + GITHUB_REPOSITORY === 'vercel/next.js' && + (GITHUB_REF || '').includes('canary'), } // get comment diff --git a/.github/actions/next-stats-action/src/prepare/repo-setup.js b/.github/actions/next-stats-action/src/prepare/repo-setup.js index 6f7b722ec6ec4..534ab34229e53 100644 --- a/.github/actions/next-stats-action/src/prepare/repo-setup.js +++ b/.github/actions/next-stats-action/src/prepare/repo-setup.js @@ -3,6 +3,7 @@ const fs = require('fs-extra') const exec = require('../util/exec') const { remove } = require('fs-extra') const logger = require('../util/logger') +const semver = require('semver') module.exports = (actionInfo) => { return { @@ -22,8 +23,9 @@ module.exports = (actionInfo) => { const curTag = tags[i] // stable doesn't include `-canary` or `-beta` if (!curTag.includes('-') && !ref.includes(curTag)) { - lastStableTag = curTag - break + if (!lastStableTag || semver.gt(curTag, lastStableTag)) { + lastStableTag = curTag + } } } return lastStableTag @@ -69,13 +71,17 @@ module.exports = (actionInfo) => { for (const pkg of pkgs) { const pkgPath = path.join(repoDir, 'packages', pkg) const packedPkgPath = path.join(pkgPath, `${pkg}-packed.tgz`) - // pack the package with yarn - await exec(`cd ${pkgPath} && yarn pack -f ${pkg}-packed.tgz`) const pkgDataPath = path.join(pkgPath, 'package.json') const pkgData = require(pkgDataPath) const { name } = pkgData - pkgDatas.set(name, { pkgDataPath, pkgData, packedPkgPath }) + pkgDatas.set(name, { + pkgDataPath, + pkg, + pkgPath, + pkgData, + packedPkgPath, + }) pkgPaths.set(name, packedPkgPath) } @@ -93,6 +99,13 @@ module.exports = (actionInfo) => { 'utf8' ) } + + // wait to pack packages until after dependency paths have been updated + // to the correct versions + for (const pkgName of pkgDatas.keys()) { + const { pkg, pkgPath } = pkgDatas.get(pkgName) + await exec(`cd ${pkgPath} && yarn pack -f ${pkg}-packed.tgz`) + } return pkgPaths }, } diff --git a/.github/actions/next-stats-action/src/run/index.js b/.github/actions/next-stats-action/src/run/index.js index c1556b13628e9..e2159e9a89bf2 100644 --- a/.github/actions/next-stats-action/src/run/index.js +++ b/.github/actions/next-stats-action/src/run/index.js @@ -6,7 +6,7 @@ const logger = require('../util/logger') const getDirSize = require('./get-dir-size') const collectStats = require('./collect-stats') const collectDiffs = require('./collect-diffs') -const { statsAppDir, diffRepoDir, mainRepoDir } = require('../constants') +const { statsAppDir, diffRepoDir, yarnEnvValues } = require('../constants') async function runConfigs( configs = [], @@ -26,18 +26,14 @@ async function runConfigs( let curStats = { General: { buildDuration: null, + buildDurationCached: null, nodeModulesSize: null, }, } // if stats-config is in root of project we're analyzing // the whole project so copy from each repo - const curStatsAppPath = - relativeStatsAppDir === './' - ? mainRepoStats - ? diffRepoDir - : mainRepoDir - : path.join(diffRepoDir, relativeStatsAppDir) + const curStatsAppPath = path.join(diffRepoDir, relativeStatsAppDir) // clean statsAppDir await fs.remove(statsAppDir) @@ -60,18 +56,25 @@ async function runConfigs( ) } - const buildStart = new Date().getTime() - await exec(`cd ${statsAppDir} && ${statsConfig.appBuildCommand}`) - curStats.General.buildDuration = new Date().getTime() - buildStart + const buildStart = Date.now() + await exec(`cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, false, { + env: yarnEnvValues, + }) + curStats.General.buildDuration = Date.now() - buildStart // apply renames to get deterministic output names for (const rename of config.renames) { const results = await glob(rename.srcGlob, { cwd: statsAppDir }) - if (results.length === 0 || results[0] === rename.dest) continue - await fs.move( - path.join(statsAppDir, results[0]), - path.join(statsAppDir, rename.dest) - ) + for (const result of results) { + let dest = rename.removeHash + ? result.replace(/(\.|-)[0-9a-f]{20}(\.|-)/g, '$1HASH$2') + : rename.dest + if (result === dest) continue + await fs.move( + path.join(statsAppDir, result), + path.join(statsAppDir, dest) + ) + } } const collectedStats = await collectStats(config, statsConfig) @@ -83,19 +86,17 @@ async function runConfigs( const applyRenames = (renames, stats) => { if (renames) { for (const rename of renames) { + let { cur, prev } = rename + cur = path.basename(cur) + prev = path.basename(prev) + Object.keys(stats).forEach((group) => { - Object.keys(stats[group]).forEach((item) => { - let { cur, prev } = rename - cur = path.basename(cur) - prev = path.basename(prev) - - if (cur === item) { - stats[group][prev] = stats[group][item] - stats[group][prev + ' gzip'] = stats[group][item + ' gzip'] - delete stats[group][item] - delete stats[group][item + ' gzip'] - } - }) + if (stats[group][cur]) { + stats[group][prev] = stats[group][cur] + stats[group][prev + ' gzip'] = stats[group][cur + ' gzip'] + delete stats[group][cur] + delete stats[group][cur + ' gzip'] + } }) } } @@ -149,6 +150,12 @@ async function runConfigs( /* eslint-disable-next-line */ mainRepoStats = curStats } + + const secondBuildStart = Date.now() + await exec(`cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, false, { + env: yarnEnvValues, + }) + curStats.General.buildDurationCached = Date.now() - secondBuildStart } logger(`Finished running: ${config.title}`) @@ -182,7 +189,11 @@ async function linkPkgs(pkgDir = '', pkgPaths) { } } await fs.writeFile(pkgJsonPath, JSON.stringify(pkgData, null, 2), 'utf8') - await exec(`cd ${pkgDir} && yarn install`) + + await fs.remove(yarnEnvValues.YARN_CACHE_FOLDER) + await exec(`cd ${pkgDir} && yarn install`, false, { + env: yarnEnvValues, + }) } module.exports = runConfigs diff --git a/.github/actions/next-stats-action/src/util/exec.js b/.github/actions/next-stats-action/src/util/exec.js index ea16221b9056b..689bd5b2952aa 100644 --- a/.github/actions/next-stats-action/src/util/exec.js +++ b/.github/actions/next-stats-action/src/util/exec.js @@ -9,9 +9,13 @@ const env = { PR_STATS_COMMENT_TOKEN: '', } -function exec(command, noLog = false) { +function exec(command, noLog = false, opts = {}) { if (!noLog) logger(`exec: ${command}`) - return execP(command, { env, timeout: 180 * 1000 }) + return execP(command, { + timeout: 180 * 1000, + ...opts, + env: { ...env, ...opts.env }, + }) } exec.spawn = function spawn(command = '', opts = {}) { diff --git a/.github/labeler.json b/.github/labeler.json new file mode 100644 index 0000000000000..1499d280fb296 --- /dev/null +++ b/.github/labeler.json @@ -0,0 +1,32 @@ +{ + "labels": { + "type: example": ["examples/**"], + "type: documentation": ["docs/**", "errors/**"], + "type: create-next-app": ["packages/create-next-app/**"], + "type: next": [ + "packages/next/**", + "packages/react-dev-overlay/**", + "packages/react-refresh-utils/**", + "packages/next-codemod/**" + ], + "created-by: Chrome Aurora": [ + { "type": "user", "pattern": "spanicker" }, + { "type": "user", "pattern": "housseindjirdeh" }, + { "type": "user", "pattern": "devknoll" }, + { "type": "user", "pattern": "janicklas-ralph" }, + { "type": "user", "pattern": "atcastle" }, + { "type": "user", "pattern": "kyliau" }, + { "type": "user", "pattern": "kara" } + ], + "created-by: Next.js team": [ + { "type": "user", "pattern": "ijjk" }, + { "type": "user", "pattern": "padmaia" }, + { "type": "user", "pattern": "huozhi" }, + { "type": "user", "pattern": "shuding" }, + { "type": "user", "pattern": "sokra" }, + { "type": "user", "pattern": "styfle" }, + { "type": "user", "pattern": "leerob" }, + { "type": "user", "pattern": "timneutkens" } + ] + } +} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000000..9e9b995c92dd8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,24 @@ + + +## Bug + +- [ ] Related issues linked using `fixes #number` +- [ ] Integration tests added +- [ ] Errors have helpful link attached, see `contributing.md` + +## Feature + +- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. +- [ ] Related issues linked using `fixes #number` +- [ ] Integration tests added +- [ ] Documentation added +- [ ] Telemetry added. In case of a feature if it's used or not. +- [ ] Errors have helpful link attached, see `contributing.md` + +## Documentation / Examples + +- [ ] Make sure the linting passes diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml new file mode 100644 index 0000000000000..dcf21e7da8fdd --- /dev/null +++ b/.github/workflows/build_native.yml @@ -0,0 +1,90 @@ +on: workflow_dispatch + +name: Build next-swc native binaries + +jobs: + build-native: + strategy: + matrix: + os: [ubuntu-18.04, macos-latest, windows-latest] + description: [default] + include: + - os: ubuntu-18.04 + target: x86_64-unknown-linux-gnu + - os: windows-latest + target: x86_64-pc-windows-msvc + - os: macos-latest + target: x86_64-apple-darwin + - os: macos-latest + target: aarch64-apple-darwin + description: m1 + + name: next-swc - ${{ matrix.os }} - ${{ matrix.target }} - node@14 + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + - name: Install + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-03-25 + target: ${{ matrix.target }} + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: stable-${{ matrix.os }}-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: stable-${{ matrix.os }}-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + - name: Cache native binary + id: binary-cache + uses: actions/cache@v2 + with: + path: packages/next/native/** + key: next-swc-nightly-2021-03-25-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_native.yml', 'packages/next/build/swc/**') }} + - name: Cross build aarch64 setup + if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: | + sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; + export CC=$(xcrun -f clang); + export CXX=$(xcrun -f clang++); + SYSROOT=$(xcrun --sdk macosx --show-sdk-path); + export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; + - name: 'Build' + if: steps.binary-cache.outputs.cache-hit != true + run: yarn build-native --target ${{ matrix.target }} + env: + MACOSX_DEPLOYMENT_TARGET: '10.13' + working-directory: packages/next + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next/native/next-swc.*.node + - name: Clear the cargo caches + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache + commit: + needs: build-native + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@v2 + with: + name: next-swc-binaries + path: packages/next/native + - uses: EndBug/add-and-commit@v7 + with: + add: 'packages/next/native --force' + message: 'Build next-swc binaries' diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index b520bba998e63..23a54e204b631 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -1,97 +1,293 @@ on: push: - branches: [canary] + branches: [canary, next-11] + tags: [v*] pull_request: types: [opened, synchronize] name: Build, test, and deploy jobs: + check-examples: + name: Check examples + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install moreutils + run: sudo apt install moreutils + - name: Check examples + run: ./scripts/check-examples.sh + build: runs-on: ubuntu-latest env: NEXT_TELEMETRY_DISABLED: 1 + outputs: + docsChange: ${{ steps.docs-change.outputs.DOCS_CHANGE }} steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + - uses: actions/checkout@v2 + with: + fetch-depth: 25 + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - run: yarn install --frozen-lockfile --check-files - - uses: actions/cache@v1 + # - run: node run-tests.js --timings --write-timings -g 1/1 + - name: Check docs only change + run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') + id: docs-change + - run: echo ${{steps.docs-change.outputs.DOCS_CHANGE}} + - uses: actions/cache@v2 id: cache-build with: - path: '.' + path: ./* key: ${{ github.sha }} lint: runs-on: ubuntu-latest needs: build steps: - - uses: actions/cache@v1 + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} + + - run: ./scripts/check-manifests.js - run: yarn lint checkPrecompiled: name: Check Pre-compiled runs-on: ubuntu-latest - needs: build + needs: [build, build-native] env: NEXT_TELEMETRY_DISABLED: 1 steps: - - uses: actions/cache@v1 + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - - run: ./check-pre-compiled.sh + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-binaries + path: packages/next/build/swc/dist + # Only check linux build for now, mac builds can sometimes be different even with the same code + - run: | + mv ./packages/next/build/swc/dist/next-swc.linux-x64-gnu.node \ + ./packages/next/native/next-swc.linux-x64-gnu.node + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + - run: ./scripts/check-pre-compiled.sh + if: ${{needs.build.outputs.docsChange != 'docs only change'}} - testAll: - name: Test All + testUnit: + name: Test Unit runs-on: ubuntu-latest needs: build env: NEXT_TELEMETRY_DISABLED: 1 NEXT_TEST_JOB: 1 HEADLESS: true - strategy: - fail-fast: false - matrix: - group: [1, 2, 3, 4, 5, 6] steps: - - uses: actions/cache@v1 + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - # TODO: remove after we fix watchpack watching too much - - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + - run: node run-tests.js --type unit + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + # testIntegration: + # name: Test Integration + # runs-on: ubuntu-latest + # needs: build + # env: + # NEXT_TELEMETRY_DISABLED: 1 + # NEXT_TEST_JOB: 1 + # HEADLESS: true + # strategy: + # fail-fast: false + # matrix: + # group: [1, 2, 3, 4, 5, 6] + # steps: + # - name: Setup node + # uses: actions/setup-node@v2 + # if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + # with: + # node-version: 14 + + # - run: echo ${{needs.build.outputs.docsChange}} + + # # https://github.com/actions/virtual-environments/issues/1187 + # - name: tune linux network + # run: sudo ethtool -K eth0 tx off rx off + + # - uses: actions/cache@v2 + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + # id: restore-build + # with: + # path: ./* + # key: ${{ github.sha }} + + # # TODO: remove after we fix watchpack watching too much + # - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + # - run: xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/6 -c 3 + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + # testElectron: + # name: Test Electron + # runs-on: ubuntu-latest + # needs: build + # env: + # NEXT_TELEMETRY_DISABLED: 1 + # NEXT_TEST_JOB: 1 + # HEADLESS: true + # TEST_ELECTRON: 1 + # steps: + # - name: Setup node + # uses: actions/setup-node@v2 + # if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + # with: + # node-version: 14 + + # - uses: actions/cache@v2 + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + # id: restore-build + # with: + # path: ./* + # key: ${{ github.sha }} + + # # TODO: remove after we fix watchpack watching too much + # - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} - - run: node run-tests.js --timings -g ${{ matrix.group }}/6 -c 3 + # - run: cd test/integration/with-electron/app && yarn + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + # - run: xvfb-run node run-tests.js test/integration/with-electron/test/index.test.js + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + # testYarnPnP: + # runs-on: ubuntu-latest + # needs: build + # env: + # NODE_OPTIONS: '--unhandled-rejections=strict' + # YARN_COMPRESSION_LEVEL: '0' + # steps: + # - name: Setup node + # uses: actions/setup-node@v2 + # if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + # with: + # node-version: 14 + + # - uses: actions/cache@v2 + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + # id: restore-build + # with: + # path: ./* + # key: ${{ github.sha }} + + # - run: bash ./scripts/test-pnp.sh + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} testsPass: name: thank you, next runs-on: ubuntu-latest - needs: [lint, checkPrecompiled, testAll] + needs: [lint, checkPrecompiled, testUnit] steps: - run: exit 0 + # testLegacyWebpack: + # name: Webpack 4 (Basic, Production, Acceptance) + # runs-on: ubuntu-latest + # needs: build + # env: + # NEXT_TELEMETRY_DISABLED: 1 + # NEXT_TEST_JOB: 1 + # HEADLESS: true + # NEXT_PRIVATE_TEST_WEBPACK4_MODE: 1 + + # steps: + # - name: Setup node + # uses: actions/setup-node@v2 + # if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + # with: + # node-version: 14 + + # # https://github.com/actions/virtual-environments/issues/1187 + # - name: tune linux network + # run: sudo ethtool -K eth0 tx off rx off + + # - uses: actions/cache@v2 + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + # id: restore-build + # with: + # path: ./* + # key: ${{ github.sha }} + + # - run: xvfb-run node run-tests.js test/integration/{basic,fallback-modules,link-ref,production,async-modules,font-optimization,ssr-ctx}/test/index.test.js test/acceptance/*.test.js + # if: ${{needs.build.outputs.docsChange != 'docs only change'}} + testFirefox: name: Test Firefox (production) runs-on: ubuntu-latest needs: build env: HEADLESS: true - BROWSERNAME: 'firefox' + BROWSER_NAME: 'firefox' NEXT_TELEMETRY_DISABLED: 1 steps: - - uses: actions/cache@v1 + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - - run: node run-tests.js test/integration/production/test/index.test.js + - run: node run-tests.js -c 1 test/integration/production/test/index.test.js + if: ${{needs.build.outputs.docsChange != 'docs only change'}} testSafari: name: Test Safari (production) @@ -99,18 +295,30 @@ jobs: needs: build env: BROWSERSTACK: true - BROWSERNAME: 'safari' + BROWSER_NAME: 'safari' NEXT_TELEMETRY_DISABLED: 1 SKIP_LOCAL_SELENIUM_SERVER: true BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} steps: - - uses: actions/cache@v1 + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production/test/index.test.js' + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js -c 1 test/integration/production/test/index.test.js' + if: ${{needs.build.outputs.docsChange != 'docs only change'}} testSafariOld: name: Test Safari 10.1 (nav) @@ -119,30 +327,192 @@ jobs: env: BROWSERSTACK: true LEGACY_SAFARI: true - BROWSERNAME: 'safari' + BROWSER_NAME: 'safari' NEXT_TELEMETRY_DISABLED: 1 SKIP_LOCAL_SELENIUM_SERVER: true BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} steps: - - uses: actions/cache@v1 + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production-nav/test/index.test.js' + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js -c 1 test/integration/production-nav/test/index.test.js' + if: ${{needs.build.outputs.docsChange != 'docs only change'}} publishRelease: + if: ${{ startsWith(github.ref, 'refs/tags/v') }} name: Potentially publish release runs-on: ubuntu-latest - needs: [testsPass] + needs: [build, build-native] env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} + steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }} + - uses: actions/download-artifact@v2 + with: + name: next-swc-binaries + path: packages/next/build/swc/dist + - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + - run: ./scripts/publish-native.js $GITHUB_REF + - run: ./scripts/publish-release.sh + + releaseStats: + name: Release Stats + runs-on: ubuntu-latest + needs: [publishRelease] steps: - - uses: actions/cache@v1 + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} + - run: ./scripts/release-stats.sh + - uses: ./.github/actions/next-stats-action + env: + PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} - - run: ./publish-release.sh + build-native: + strategy: + matrix: + os: [ubuntu-18.04, macos-latest, windows-latest] + description: [default] + include: + - os: ubuntu-18.04 + target: x86_64-unknown-linux-gnu + - os: windows-latest + target: x86_64-pc-windows-msvc + - os: macos-latest + target: x86_64-apple-darwin + - os: macos-latest + target: aarch64-apple-darwin + description: m1 + + name: next-swc - ${{ matrix.os }} - ${{ matrix.target }} - node@14 + runs-on: ${{ matrix.os }} + + steps: + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + if: ${{ matrix.os == 'ubuntu-18.04' }} + - name: tune windows network + run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6 + if: ${{ matrix.os == 'windows-latest' }} + - name: tune mac network + run: sudo sysctl -w net.link.generic.system.hwcksum_tx=0 && sudo sysctl -w net.link.generic.system.hwcksum_rx=0 + if: ${{ matrix.os == 'macos-latest' }} + + - uses: actions/checkout@v2 + with: + fetch-depth: 25 + - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') + id: docs-change + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + node-version: 14 + check-latest: true + - name: Install + uses: actions-rs/toolchain@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + profile: minimal + toolchain: nightly-2021-03-25 + target: ${{ matrix.target }} + - name: Cache cargo registry + uses: actions/cache@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: ~/.cargo/registry + key: stable-${{ matrix.os }}-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: ~/.cargo/git + key: stable-${{ matrix.os }}-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + - name: Cache native binary + id: binary-cache + uses: actions/cache@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: packages/next/native/next-swc.*.node + key: next-swc-nightly-2021-03-25-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_test_deploy.yml', 'packages/next/build/swc/**') }} + - name: Cross build aarch64 setup + if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: | + sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; + export CC=$(xcrun -f clang); + export CXX=$(xcrun -f clang++); + SYSROOT=$(xcrun --sdk macosx --show-sdk-path); + export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; + - name: 'Build' + if: ${{ steps.binary-cache.outputs.cache-hit != 'true' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: yarn build-native --target ${{ matrix.target }} + env: + MACOSX_DEPLOYMENT_TARGET: '10.13' + working-directory: packages/next + - name: Upload artifact + uses: actions/upload-artifact@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + name: next-swc-binaries + path: packages/next/native/next-swc.*.node + - name: Clear the cargo caches + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache + + test-native: + name: Unit Test Native Code + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 25 + - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') + id: docs-change + - name: Install + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2021-03-25 + profile: minimal + - run: cd packages/next/build/swc && cargo test + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml new file mode 100644 index 0000000000000..ce58d11cab7c9 --- /dev/null +++ b/.github/workflows/cancel.yml @@ -0,0 +1,17 @@ +name: Cancel +on: + pull_request_target: + types: + - edited + - synchronize + +jobs: + cancel: + name: 'Cancel Previous Runs' + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - uses: styfle/cancel-workflow-action@0.5.0 + with: + workflow_id: 444921, 444987 + access_token: ${{ github.token }} diff --git a/.github/workflows/pull_request_stats.yml b/.github/workflows/pull_request_stats.yml index 429e9c947aed4..4c4874cdb9f48 100644 --- a/.github/workflows/pull_request_stats.yml +++ b/.github/workflows/pull_request_stats.yml @@ -10,4 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 25 + + - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') + id: docs-change - uses: ./.github/actions/next-stats-action + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} diff --git a/.github/workflows/release_stats.yml b/.github/workflows/release_stats.yml deleted file mode 100644 index c734488bdc264..0000000000000 --- a/.github/workflows/release_stats.yml +++ /dev/null @@ -1,13 +0,0 @@ -on: release - -name: Generate Release Stats - -jobs: - prStats: - name: Release Stats - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: ./.github/actions/next-stats-action - env: - PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml new file mode 100644 index 0000000000000..573425c04aaea --- /dev/null +++ b/.github/workflows/test_macos.yml @@ -0,0 +1,27 @@ +on: + push: + branches: [canary] + paths-ignore: + - 'bench/**' + - 'docs/**' + - 'errors/**' + - 'examples/**' + +name: Test macOS + +jobs: + testMacOS: + name: macOS (Basic, Production, Acceptance) + runs-on: macos-latest + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + HEADLESS: true + + steps: + - uses: actions/checkout@v2 + - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - run: yarn install --frozen-lockfile --check-files || yarn install --frozen-lockfile --check-files + - run: node run-tests.js test/integration/production/test/index.test.js + - run: node run-tests.js test/integration/basic/test/index.test.js + - run: node run-tests.js test/acceptance/* diff --git a/.github/workflows/test_react_experimental.yml b/.github/workflows/test_react_experimental.yml new file mode 100644 index 0000000000000..bbfb890544d67 --- /dev/null +++ b/.github/workflows/test_react_experimental.yml @@ -0,0 +1,55 @@ +on: + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '0 0,12 * * *' + +name: Test react@experimental + +jobs: + # build: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + + # - run: yarn install --frozen-lockfile --check-files + # env: + # NEXT_TELEMETRY_DISABLED: 1 + + # - run: yarn upgrade react@next react-dom@next -W --dev + + # - uses: actions/cache@v2 + # id: cache-build + # with: + # path: ./* + # key: ${{ github.sha }} + + testAll: + name: Test All + runs-on: ubuntu-latest + # needs: build + env: + NEXT_TELEMETRY_DISABLED: 1 + HEADLESS: true + NEXT_PRIVATE_SKIP_SIZE_TESTS: true + NEXT_PRIVATE_REACT_ROOT: 1 + strategy: + fail-fast: false + matrix: + group: [1, 2, 3, 4, 5, 6] + steps: + # - uses: actions/cache@v2 + # id: restore-build + # with: + # path: ./* + # key: ${{ github.sha }} + + - uses: actions/checkout@v2 + + - run: yarn install --frozen-lockfile --check-files + + - run: yarn upgrade react@experimental react-dom@experimental -W --dev + + # TODO: remove after we fix watchpack watching too much + - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + + - run: node run-tests.js --timings -g ${{ matrix.group }}/6 -c 3 diff --git a/.github/workflows/test_react_next.yml b/.github/workflows/test_react_next.yml index 443b0e25af518..4845ca86c9a5a 100644 --- a/.github/workflows/test_react_next.yml +++ b/.github/workflows/test_react_next.yml @@ -17,10 +17,10 @@ jobs: # - run: yarn upgrade react@next react-dom@next -W --dev - # - uses: actions/cache@v1 + # - uses: actions/cache@v2 # id: cache-build # with: - # path: '.' + # path: ./* # key: ${{ github.sha }} testAll: @@ -30,15 +30,17 @@ jobs: env: NEXT_TELEMETRY_DISABLED: 1 HEADLESS: true + NEXT_PRIVATE_SKIP_SIZE_TESTS: true + NEXT_PRIVATE_REACT_ROOT: 1 strategy: fail-fast: false matrix: group: [1, 2, 3, 4, 5, 6] steps: - # - uses: actions/cache@v1 + # - uses: actions/cache@v2 # id: restore-build # with: - # path: '.' + # path: ./* # key: ${{ github.sha }} - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 26fc9ca3dd6da..011b7fffcbbde 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # build output dist .next +target # dependencies node_modules @@ -21,12 +22,19 @@ coverage test/**/out* test/**/next-env.d.ts .DS_Store +/e2e-tests # Editors **/.idea +**/.#* # examples examples/**/out examples/**/.env*.local pr-stats.md +test-timings.json + +# Vercel +.vercel +.now diff --git a/.prettierignore b/.prettierignore index b1adbf783a5a5..101632425ad1d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,10 +2,18 @@ node_modules **/.next/** **/_next/** **/dist/** +packages/next/bundles/webpack/packages/*.runtime.js packages/next/compiled/** packages/react-refresh-utils/**/*.js packages/react-refresh-utils/**/*.d.ts packages/react-dev-overlay/lib/** **/__tmp__/** lerna.json -.github/actions/next-stats-action/.work \ No newline at end of file +.github/actions/next-stats-action/.work +packages/next-codemod/transforms/__testfixtures__/**/* +packages/next-codemod/transforms/__tests__/**/* +packages/next-codemod/**/*.js +packages/next-codemod/**/*.d.ts +packages/next-env/**/*.d.ts +test-timings.json +test/**/out/** diff --git a/.prettierignore_staged b/.prettierignore_staged index 3e87a0d626523..e888b26919139 100644 --- a/.prettierignore_staged +++ b/.prettierignore_staged @@ -2,4 +2,7 @@ **/_next/** **/dist/** packages/next/compiled/**/* +packages/next/bundles/webpack/packages/*.runtime.js lerna.json +packages/next-codemod/transforms/__testfixtures__/**/* +packages/next-codemod/transforms/__tests__/**/* diff --git a/.prettierrc.json b/.prettierrc.json index 0b4951088a670..fd496a820ea94 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,4 @@ { "singleQuote": true, - "semi": false, - "trailingComma": "es5" + "semi": false } diff --git a/.vscode/launch.json b/.vscode/launch.json index 512d385b8e434..9296ff142e951 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,6 +12,7 @@ "runtimeExecutable": "yarn", "runtimeArgs": ["run", "debug", "dev", "test/integration/basic"], "skipFiles": ["/**"], + "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"], "port": 9229 }, { @@ -22,7 +23,19 @@ "runtimeExecutable": "yarn", "runtimeArgs": ["run", "debug", "build", "test/integration/basic"], "skipFiles": ["/**"], - "port": 9229 + "port": 9229, + "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] + }, + { + "name": "Launch app build trace jaeger", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "yarn", + "runtimeArgs": ["run", "clean-trace-jaeger"], + "skipFiles": ["/**"], + "port": 9229, + "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] }, { "name": "Launch app production", @@ -41,6 +54,16 @@ "port": 9229, "skipFiles": ["/**"], "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] + }, + { + "name": "Launch this example", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "yarn", + "runtimeArgs": ["run", "debug", "dev", "${fileDirname}"], + "skipFiles": ["/**"], + "port": 9229 } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index e521849f2cdba..9b9d414d6ec53 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,6 @@ "javascriptreact", { "language": "typescript", "autoFix": true }, { "language": "typescriptreact", "autoFix": true } - ] + ], + "debug.javascript.unmapMissingSources": true } diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..294bff136eef4 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1 @@ +Visit https://vercel.com/security to view the disclosure policy. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0550c30614c67..dc023d97813be 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,66 +1,149 @@ +trigger: + # Only run latest commit for branches: + batch: true + # Do not run Azure CI for docs-only/example-only changes: + paths: + include: + - '*' + exclude: + - bench + - docs + - errors + - examples + # Do not run Azure on `canary`, `master`, or release tags. This unnecessarily + # increases the backlog, and the change was already tested on the PR. + branches: + include: + - '*' + exclude: + - canary + - master + - refs/tags/* + +pr: + # Do not run Azure CI for docs-only/example-only changes: + paths: + include: + - '*' + exclude: + - bench + - docs + - errors + - examples + variables: YARN_CACHE_FOLDER: $(Pipeline.Workspace)/.yarn NEXT_TELEMETRY_DISABLED: '1' - node_version: ^10.10.0 - -jobs: - - job: test_ie11 - pool: - vmImage: 'windows-2019' - steps: - - task: NodeTool@0 - inputs: - versionSpec: $(node_version) - displayName: 'Install Node.js' - - - task: CacheBeta@0 - inputs: - key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) - displayName: Cache Yarn packages - - - script: | - yarn install --frozen-lockfile --check-files - displayName: 'Install dependencies' - - - script: | - yarn testie --forceExit test/integration/production/ - displayName: 'Run tests' - - - job: test_chrome - pool: - vmImage: 'windows-2019' - strategy: - maxParallel: 10 - matrix: - node-10-1: - group: 1/4 - node-10-2: - group: 2/4 - node-10-3: - group: 3/4 - node-10-4: - group: 4/4 - steps: - - script: | - wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value - displayName: 'List Chrome version' - - - task: NodeTool@0 - inputs: - versionSpec: $(node_version) - displayName: 'Install Node.js' + node_version: ^12.0.0 - - task: CacheBeta@0 - inputs: - key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) - displayName: Cache Yarn packages +stages: + - stage: Build + jobs: + - job: build + pool: + vmImage: 'windows-2019' + steps: + - script: echo $(Agent.BuildDirectory) + - script: dir + - script: dir $(System.DefaultWorkingDirectory) + - script: echo $(Build.SourceVersion) + - powershell: Get-MpComputerStatus + - task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: 'Install Node.js' + - task: Cache@2 + inputs: + # use deterministic cache key that is specific + # to this test run + key: $(Build.SourceVersion) + path: $(System.DefaultWorkingDirectory) + displayName: Cache Build + - script: | + yarn install --frozen-lockfile --check-files + displayName: 'Install dependencies' + - script: | + node run-tests.js --timings --write-timings --azure -g 1/1 + displayName: 'Fetch test timing data' - - script: | - yarn install --frozen-lockfile --check-files - displayName: 'Install dependencies' + - stage: Test + dependsOn: Build + jobs: + - job: test_ie11 + pool: + vmImage: 'windows-2019' + variables: + BROWSER_NAME: internet explorer + steps: + - checkout: none + - task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: 'Install Node.js' + - task: Cache@2 + inputs: + # use deterministic cache key that is specific + # to this test run + key: $(Build.SourceVersion) + path: $(System.DefaultWorkingDirectory) + displayName: Cache Build + - script: | + node run-tests.js -c 1 test/integration/production/test/index.test.js test/integration/css-client-nav/test/index.test.js test/integration/rewrites-has-condition/test/index.test.js + displayName: 'Run tests' - - script: | - node run-tests.js -g $(group) --timings --azure - displayName: 'Run tests' + - job: test_unit + pool: + vmImage: 'windows-2019' + steps: + - checkout: none + - script: | + wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value + displayName: 'List Chrome version' + - task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: 'Install Node.js' + - task: Cache@2 + inputs: + # use deterministic cache key that is specific + # to this test run + key: $(Build.SourceVersion) + path: $(System.DefaultWorkingDirectory) + displayName: Cache Build + - script: | + node run-tests.js --type unit + displayName: 'Run tests' + # TODO: investigate re-enabling when stability matches running in + # tests in ubuntu environment + # - job: test_chrome_integration + # pool: + # vmImage: 'windows-2019' + # strategy: + # matrix: + # nodejs-1: + # group: 1/4 + # nodejs-2: + # group: 2/4 + # nodejs-3: + # group: 3/4 + # nodejs-4: + # group: 4/4 + # steps: + # - checkout: none + # - script: | + # wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value + # displayName: 'List Chrome version' + # - task: NodeTool@0 + # inputs: + # versionSpec: $(node_version) + # displayName: 'Install Node.js' + # - task: Cache@2 + # inputs: + # # use deterministic cache key that is specific + # # to this test run + # key: $(Build.SourceVersion) + # path: $(System.DefaultWorkingDirectory) + # displayName: Cache Build + # - script: | + # node run-tests.js -g $(group) --timings --azure + # displayName: 'Run tests' diff --git a/bench/capture-trace.js b/bench/capture-trace.js new file mode 100644 index 0000000000000..d7419745c3a0e --- /dev/null +++ b/bench/capture-trace.js @@ -0,0 +1,66 @@ +import { createServer } from 'http' +import { writeFileSync } from 'fs' + +const PORT = 9411 +const HOST = '0.0.0.0' + +const traces = [] + +const onReady = () => console.log(`Listening on http://${HOST}:${PORT}`) +const onRequest = async (req, res) => { + if ( + req.method !== 'POST' || + req.url !== '/api/v2/spans' || + (req.headers && req.headers['content-type']) !== 'application/json' + ) { + res.writeHead(200) + return res.end() + } + + try { + const body = JSON.parse(await getBody(req)) + for (const traceEvent of body) { + traces.push(traceEvent) + } + res.writeHead(200) + } catch (err) { + console.warn(err) + res.writeHead(500) + } + + res.end() +} + +const getBody = (req) => + new Promise((resolve, reject) => { + let data = '' + req.on('data', (chunk) => { + data += chunk + }) + req.on('end', () => { + if (!req.complete) { + return reject('Connection terminated before body was received.') + } + resolve(data) + }) + req.on('aborted', () => reject('Connection aborted.')) + req.on('error', () => reject('Connection error.')) + }) + +const main = () => { + const args = process.argv.slice(2) + const outFile = args[0] || `./trace-${Date.now()}.json` + + process.on('SIGINT', () => { + console.log(`\nSaving to ${outFile}...`) + writeFileSync(outFile, JSON.stringify(traces, null, 2)) + process.exit() + }) + + const server = createServer(onRequest) + server.listen(PORT, HOST, onReady) +} + +if (require.main === module) { + main() +} diff --git a/bench/package.json b/bench/package.json index eb3c593c98f8c..d311332afdefa 100644 --- a/bench/package.json +++ b/bench/package.json @@ -8,7 +8,7 @@ "bench:recursive-copy": "node recursive-copy/run" }, "dependencies": { - "fs-extra": "7.0.1", - "recursive-copy": "2.0.10" + "fs-extra": "10.0.0", + "recursive-copy": "2.0.11" } } diff --git a/bench/readdir/glob.js b/bench/readdir/glob.js index 1c409ad384ba5..170f4fb050eb5 100644 --- a/bench/readdir/glob.js +++ b/bench/readdir/glob.js @@ -1,6 +1,7 @@ -const { join } = require('path') -const { promisify } = require('util') -const globMod = require('glob') +import { join } from 'path' +import { promisify } from 'util' +import globMod from 'glob' + const glob = promisify(globMod) const resolveDataDir = join(__dirname, 'fixtures', '**/*') diff --git a/bench/readdir/recursive-readdir.js b/bench/readdir/recursive-readdir.js index 871256707ddb6..2167f30336553 100644 --- a/bench/readdir/recursive-readdir.js +++ b/bench/readdir/recursive-readdir.js @@ -1,5 +1,5 @@ -const { join } = require('path') -const { recursiveReadDir } = require('next/dist/lib/recursive-readdir') +import { join } from 'path' +import { recursiveReadDir } from 'next/dist/lib/recursive-readdir' const resolveDataDir = join(__dirname, 'fixtures') async function test() { diff --git a/bench/recursive-copy/run.js b/bench/recursive-copy/run.js index c4bad36c88df3..ab12258004724 100644 --- a/bench/recursive-copy/run.js +++ b/bench/recursive-copy/run.js @@ -1,18 +1,14 @@ -const { join } = require('path') -const fs = require('fs-extra') - -const recursiveCopyNpm = require('recursive-copy') - -const { - recursiveCopy: recursiveCopyCustom, -} = require('next/dist/lib/recursive-copy') +import { join } from 'path' +import { ensureDir, outputFile, remove } from 'fs-extra' +import recursiveCopyNpm from 'recursive-copy' +import { recursiveCopy as recursiveCopyCustom } from 'next/dist/lib/recursive-copy' const fixturesDir = join(__dirname, 'fixtures') const srcDir = join(fixturesDir, 'src') const destDir = join(fixturesDir, 'dest') const createSrcFolder = async () => { - await fs.ensureDir(srcDir) + await ensureDir(srcDir) const files = new Array(100) .fill(undefined) @@ -20,7 +16,7 @@ const createSrcFolder = async () => { join(srcDir, `folder${i % 5}`, `folder${i + (1 % 5)}`, `file${i}`) ) - await Promise.all(files.map((file) => fs.outputFile(file, 'hello'))) + await Promise.all(files.map((file) => outputFile(file, 'hello'))) } async function run(fn) { @@ -38,7 +34,7 @@ async function run(fn) { for (let i = 0; i < 10; i++) { const t = await test() - await fs.remove(destDir) + await remove(destDir) ts.push(t) } @@ -57,7 +53,7 @@ async function main() { console.log('test recursive-copy custom implementation') await run(recursiveCopyCustom) - await fs.remove(fixturesDir) + await remove(fixturesDir) } main() diff --git a/bench/recursive-delete/recursive-delete.js b/bench/recursive-delete/recursive-delete.js index 8989739c9039f..e23ed3e6f26a6 100644 --- a/bench/recursive-delete/recursive-delete.js +++ b/bench/recursive-delete/recursive-delete.js @@ -1,5 +1,5 @@ -const { join } = require('path') -const { recursiveDelete } = require('next/dist/lib/recursive-delete') +import { join } from 'path' +import { recursiveDelete } from 'next/dist/lib/recursive-delete' const resolveDataDir = join(__dirname, `fixtures-${process.argv[2]}`) async function test() { diff --git a/bench/recursive-delete/rimraf.js b/bench/recursive-delete/rimraf.js index 2b5d50457a13c..827cdaae77484 100644 --- a/bench/recursive-delete/rimraf.js +++ b/bench/recursive-delete/rimraf.js @@ -1,8 +1,9 @@ -const { join } = require('path') -const { promisify } = require('util') -const rimrafMod = require('rimraf') -const resolveDataDir = join(__dirname, `fixtures-${process.argv[2]}`, '**/*') +import { join } from 'path' +import { promisify } from 'util' +import rimrafMod from 'rimraf' + const rimraf = promisify(rimrafMod) +const resolveDataDir = join(__dirname, `fixtures-${process.argv[2]}`, '**/*') async function test() { const time = process.hrtime() diff --git a/check-pre-compiled.sh b/check-pre-compiled.sh deleted file mode 100755 index 431be334834d9..0000000000000 --- a/check-pre-compiled.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -yarn --cwd packages/next ncc-compiled - -# Make sure to exit with 1 if there are changes after running ncc-compiled -# step to ensure we get any changes committed - -if [[ ! -z $(git status -s) ]];then - echo "Detected changes" - git status - exit 1 -fi diff --git a/contributing.md b/contributing.md index 7bc0271e625f4..cbb3eddaa9f7e 100644 --- a/contributing.md +++ b/contributing.md @@ -1,28 +1,73 @@ # Contributing to Next.js -Our Commitment to Open Source can be found [here](https://vercel.com/oss). +Read about our [Commitment to Open Source](https://vercel.com/oss). To +contribute to [our examples](examples), please see **[Adding +examples](#adding-examples)** below. -1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. -2. Create a new branch `git checkout -b MY_BRANCH_NAME` -3. Install yarn: `npm install -g yarn` -4. Install the dependencies: `yarn` -5. Run `yarn dev` to build and watch for code changes -6. In a new terminal, run `yarn types` to compile declaration files from TypeScript -7. The development branch is `canary` (this is the branch pull requests should be made against). On a release, the relevant parts of the changes in the `canary` branch are rebased into `master`. +## Developing -> You may need to run `yarn types` again if your types get outdated. +The development branch is `canary`, and this is the branch that all pull +requests should be made against. After publishing a stable release, the changes +in the `canary` branch are rebased into `master`. The changes on the `canary` +branch are published to the `@canary` dist-tag daily. -To contribute to [our examples](examples), take a look at the [“Adding examples” section](#adding-examples). +To develop locally: -## To run tests +1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your + own GitHub account and then + [clone](https://help.github.com/articles/cloning-a-repository/) it to your + local device. +2. Create a new branch: + ``` + git checkout -b MY_BRANCH_NAME + ``` +3. Install yarn: + ``` + npm install -g yarn + ``` +4. Install the dependencies with: + ``` + yarn + ``` +5. Start developing and watch for code changes: + ``` + yarn dev + ``` +6. In a new terminal, run `yarn types` to compile declaration files from + TypeScript. + + _Note: You may need to repeat this step if your types get outdated._ + +For instructions on how to build a project with your local version of the CLI, +see **[Developing with your local version of Next.js](#developing-with-your-local-version-of-nextjs)** +below. (Naively linking the binary is not sufficient to develop locally.) + +## Building + +You can build the project, including all type definitions, with: -Make sure you have `chromedriver` installed for your Chrome version. You can install it with +```bash +yarn build +# - or - +yarn prepublish +``` + +If you need to clean the project for any reason, use `yarn clean`. + +## Testing -- `brew cask install chromedriver` on Mac OS X +Make sure you have `chromedriver` installed, and it should match your Chrome version. +You can install it with: + +- `apt install chromedriver` on Ubuntu/Debian +- `brew install --cask chromedriver` on Mac OS X - `chocolatey install chromedriver` on Windows + - Or manually download the version that matches your installed chrome version (if there's no match, download a version under it, but not above) from the [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) and add the binary to `/node_modules/.bin` -Running all tests: +You may also have to [install Rust](https://www.rust-lang.org/tools/install) and build our native packages to see all tests pass locally. We check in binaries for the most common targets and those required for CI so that most people don't have to, but if you do not see a binary for your target in `packages/next/native`, you can build it by running `yarn --cwd packages/next build-native`. If you are working on the Rust code and you need to build the binaries for ci, you can manually trigger [the workflow](https://github.com/vercel/next.js/actions/workflows/build_native.yml) to build and commit with the "Run workflow" button. + +### Running tests ```sh yarn testonly @@ -46,13 +91,13 @@ Running a specific test suite inside of the `test/integration` directory: yarn testonly --testPathPattern "production" ``` -Running just one test in the `production` test suite: +Running one test in the `production` test suite: ```sh yarn testonly --testPathPattern "production" -t "should allow etag header support" ``` -## Running the integration apps +### Running the integration apps Running examples can be done with: @@ -77,7 +122,11 @@ EXAMPLE=./test/integration/basic ) ``` -## Running your own app with locally compiled version of Next.js +## Developing with your local version of Next.js + +There are two options to develop with your local version of the codebase: + +### Set as local dependency in package.json 1. In your app's `package.json`, replace: @@ -88,7 +137,7 @@ EXAMPLE=./test/integration/basic with: ```json - "next": "file:/packages/next", + "next": "file:/path/to/next.js/packages/next", ``` 2. In your app's root directory, make sure to remove `next` from `node_modules` with: @@ -115,6 +164,42 @@ EXAMPLE=./test/integration/basic yarn install --force ``` +or + +### Develop inside the monorepo + +1. Move your app inside of the Next.js monorepo. + +2. Run with `yarn next-with-deps ./app-path-in-monorepo` + +This will use the version of `next` built inside of the Next.js monorepo and the +main `yarn dev` monorepo command can be running to make changes to the local +Next.js version at the same time (some changes might require re-running `yarn next-with-deps` to take affect). + +## Adding warning/error descriptions + +In Next.js we have a system to add helpful links to warnings and errors. + +This allows for the logged message to be short while giving a broader description and instructions on how to solve the warning/error. + +In general all warnings and errors added should have these links attached. + +Below are the steps to add a new link: + +1. Create a new markdown file under the `errors` directory based on + `errors/template.md`: + + ```shell + cp errors/template.md errors/.md + ``` + +2. Add the newly added file to `errors/manifest.json` +3. Add the following url to your warning/error: + `https://nextjs.org/docs/messages/`. + + For example, to link to `errors/api-routes-static-export.md` you use the url: + `https://nextjs.org/docs/messages/api-routes-static-export` + ## Adding examples When you add an example to the [examples](examples) directory, don’t forget to add a `README.md` file with the following format: @@ -124,48 +209,38 @@ When you add an example to the [examples](examples) directory, don’t forget to - To add additional installation instructions, please add it where appropriate. - To add additional notes, add `## Notes` section at the end. - Remove the `Deploy your own` section if your example can’t be immediately deployed to Vercel. +- Remove the `Preview` section if the example doesn't work on [StackBlitz](http://stackblitz.com/) and file an issue [here](https://github.com/stackblitz/webcontainer-core). ````markdown # Example Name Description +## Preview + +Preview the example live on [StackBlitz](http://stackblitz.com/): + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/DIRECTORY_NAME) + ## Deploy your own -Deploy the example using [Vercel](https://vercel.com/now): +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/DIRECTORY_NAME) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/DIRECTORY_NAME&project-name=DIRECTORY_NAME&repository-name=DIRECTORY_NAME) ## How to use -### Using `create-next-app` - Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: ```bash -npm init next-app --example DIRECTORY_NAME DIRECTORY_NAME-app +npx create-next-app --example DIRECTORY_NAME DIRECTORY_NAME-app # or yarn create next-app --example DIRECTORY_NAME DIRECTORY_NAME-app ``` -### Download manually - -Download the example: - -```bash -curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/DIRECTORY_NAME -cd DIRECTORY_NAME -``` - -Install it and run: +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). +```` -```bash -npm install -npm run dev -# or -yarn -yarn dev -``` +## Publishing -Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). -```` +Repository maintainers can use `yarn publish-canary` to publish a new version of all packages to npm. diff --git a/docs/advanced-features/amp-support/amp-in-static-html-export.md b/docs/advanced-features/amp-support/amp-in-static-html-export.md index dfa3afbc41f30..67351a27d9bf5 100644 --- a/docs/advanced-features/amp-support/amp-in-static-html-export.md +++ b/docs/advanced-features/amp-support/amp-in-static-html-export.md @@ -27,7 +27,7 @@ And the AMP version of your page will include a link to the HTML page: ``` -When [`exportTrailingSlash`](/docs/api-reference/next.config.js/exportPathMap.md#0cf7d6666b394c5d8d08a16a933e86ea) is enabled the exported pages for `pages/about.js` would be: +When [`trailingSlash`](/docs/api-reference/next.config.js/trailing-slash.md) is enabled the exported pages for `pages/about.js` would be: - `out/about/index.html` - HTML page - `out/about.amp/index.html` - AMP page diff --git a/docs/advanced-features/amp-support/amp-validation.md b/docs/advanced-features/amp-support/amp-validation.md index 8590243307123..5d7f7efe52b92 100644 --- a/docs/advanced-features/amp-support/amp-validation.md +++ b/docs/advanced-features/amp-support/amp-validation.md @@ -7,3 +7,27 @@ description: AMP pages are automatically validated by Next.js during development AMP pages are automatically validated with [amphtml-validator](https://www.npmjs.com/package/amphtml-validator) during development. Errors and warnings will appear in the terminal where you started Next.js. Pages are also validated during [Static HTML export](/docs/advanced-features/static-html-export.md) and any warnings / errors will be printed to the terminal. Any AMP errors will cause the export to exit with status code `1` because the export is not valid AMP. + +### Custom Validators + +You can set up custom AMP validator in `next.config.js` as shown below: + +```jsx +module.exports = { + amp: { + validator: './custom_validator.js', + }, +} +``` + +### Skip AMP Validation + +To turn off AMP validation add the following code to `next.config.js` + +```jsx +experimental: { + amp: { + skipValidation: true + } +} +``` diff --git a/docs/advanced-features/amp-support/typescript.md b/docs/advanced-features/amp-support/typescript.md index 273c943d94920..2f284c26d8035 100644 --- a/docs/advanced-features/amp-support/typescript.md +++ b/docs/advanced-features/amp-support/typescript.md @@ -6,4 +6,4 @@ description: Using AMP with TypeScript? Extend your typings to allow AMP compone AMP currently doesn't have built-in types for TypeScript, but it's in their roadmap ([#13791](https://github.com/ampproject/amphtml/issues/13791)). -As a workaround you can manually create a file called `amp.d.ts` inside your project and add the custom types described [here](https://stackoverflow.com/a/50601125). +As a workaround you can manually create a file called `amp.d.ts` inside your project and add these [custom types](https://stackoverflow.com/a/50601125). diff --git a/docs/advanced-features/automatic-static-optimization.md b/docs/advanced-features/automatic-static-optimization.md index 5685b6962207d..17a3ea787c3b9 100644 --- a/docs/advanced-features/automatic-static-optimization.md +++ b/docs/advanced-features/automatic-static-optimization.md @@ -25,17 +25,15 @@ During prerendering, the router's `query` object will be empty since we do not h `next build` will emit `.html` files for statically optimized pages. For example, the result for the page `pages/about.js` would be: ```bash -.next/server/static/${BUILD_ID}/about.html +.next/server/pages/about.html ``` And if you add `getServerSideProps` to the page, it will then be JavaScript, like so: ```bash -.next/server/static/${BUILD_ID}/about.js +.next/server/pages/about.js ``` -In development you'll know if `pages/about.js` is optimized or not thanks to the included [static optimization indicator](/docs/api-reference/next.config.js/static-optimization-indicator.md). - ## Caveats - If you have a [custom `App`](/docs/advanced-features/custom-app.md) with `getInitialProps` then this optimization will be turned off in pages without [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). diff --git a/docs/advanced-features/codemods.md b/docs/advanced-features/codemods.md new file mode 100644 index 0000000000000..f897c89f206ea --- /dev/null +++ b/docs/advanced-features/codemods.md @@ -0,0 +1,186 @@ +--- +description: Use codemods to update your codebase when upgrading Next.js to the latest version +--- + +# Next.js Codemods + +Next.js provides Codemod transformations to help upgrade your Next.js codebase when a feature is deprecated. + +Codemods are transformations that run on your codebase programmatically. This allows for a large amount of changes to be applied without having to manually go through every file. + +## Usage + +`npx @next/codemod ` + +- `transform` - name of transform, see available transforms below. +- `path` - files or directory to transform +- `--dry` Do a dry-run, no code will be edited +- `--print` Prints the changed output for comparison + +## Next.js 11 + +### `cra-to-next` (experimental) + +Migrates a Create React App project to Next.js; creating a pages directory and necessary config to match behavior. Client-side only rendering is leveraged initially to prevent breaking compatibility due to `window` usage during SSR and can be enabled seamlessly to allow gradual adoption of Next.js specific features. + +Please share any feedback related to this transform [in this discussion](https://github.com/vercel/next.js/discussions/25858). + +## Next.js 10 + +### `add-missing-react-import` + +Transforms files that do not import `React` to include the import in order for the new [React JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) to work. + +For example: + +```jsx +// my-component.js +export default class Home extends React.Component { + render() { + return
Hello World
+ } +} +``` + +Transforms into: + +```jsx +// my-component.js +import React from 'react' +export default class Home extends React.Component { + render() { + return
Hello World
+ } +} +``` + +## Next.js 9 + +### `name-default-component` + +Transforms anonymous components into named components to make sure they work with [Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh). + +For example: + +```jsx +// my-component.js +export default function () { + return
Hello World
+} +``` + +Transforms into: + +```jsx +// my-component.js +export default function MyComponent() { + return
Hello World
+} +``` + +The component will have a camel cased name based on the name of the file, and it also works with arrow functions. + +#### Usage + +Go to your project + +``` +cd path-to-your-project/ +``` + +Run the codemod: + +``` +npx @next/codemod name-default-component +``` + +### `withamp-to-config` + +Transforms the `withAmp` HOC into Next.js 9 page configuration. + +For example: + +```js +// Before +import { withAmp } from 'next/amp' + +function Home() { + return

My AMP Page

+} + +export default withAmp(Home) +``` + +```js +// After +export default function Home() { + return

My AMP Page

+} + +export const config = { + amp: true, +} +``` + +#### Usage + +Go to your project + +``` +cd path-to-your-project/ +``` + +Run the codemod: + +``` +npx @next/codemod withamp-to-config +``` + +## Next.js 6 + +### `url-to-withrouter` + +Transforms the deprecated automatically injected `url` property on top level pages to using `withRouter` and the `router` property it injects. Read more here: [https://nextjs.org/docs/messages/url-deprecated](https://nextjs.org/docs/messages/url-deprecated) + +For example: + +```js +// From +import React from 'react' +export default class extends React.Component { + render() { + const { pathname } = this.props.url + return
Current pathname: {pathname}
+ } +} +``` + +```js +// To +import React from 'react' +import { withRouter } from 'next/router' +export default withRouter( + class extends React.Component { + render() { + const { pathname } = this.props.router + return
Current pathname: {pathname}
+ } + } +) +``` + +This is one case. All the cases that are transformed (and tested) can be found in the [`__testfixtures__` directory](https://github.com/vercel/next.js/tree/canary/packages/next-codemod/transforms/__testfixtures__/url-to-withrouter). + +#### Usage + +Go to your project + +``` +cd path-to-your-project/ +``` + +Run the codemod: + +``` +npx @next/codemod url-to-withrouter +``` diff --git a/docs/advanced-features/custom-app.md b/docs/advanced-features/custom-app.md index 4a3f82ddb4bc9..71dd20af32577 100644 --- a/docs/advanced-features/custom-app.md +++ b/docs/advanced-features/custom-app.md @@ -10,7 +10,7 @@ Next.js uses the `App` component to initialize pages. You can override it and co - Keeping state when navigating pages - Custom error handling using `componentDidCatch` - Inject additional data into pages -- [Add global CSS](/docs/basic-features/built-in-css-support#adding-a-global-stylesheet) +- [Add global CSS](/docs/basic-features/built-in-css-support.md#adding-a-global-stylesheet) To override the default `App`, create the file `./pages/_app.js` as shown below: @@ -42,12 +42,14 @@ The `Component` prop is the active `page`, so whenever you navigate between rout ### Caveats -- If your app is running and you just added a custom `App`, you'll need to restart the development server. Only required if `pages/_app.js` didn't exist before. +- If your app is running and you added a custom `App`, you'll need to restart the development server. Only required if `pages/_app.js` didn't exist before. - Adding a custom `getInitialProps` in your `App` will disable [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) in pages without [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). +- When you add `getInitialProps` in your custom app, you must `import App from "next/app"`, call `App.getInitialProps(appContext)` inside `getInitialProps` and merge the returned object into the return value. +- `App` currently does not support Next.js [Data Fetching methods](/docs/basic-features/data-fetching.md) like [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering). ### TypeScript -If you’re using TypeScript, take a look at [our TypeScript documentation](/docs/basic-features/typescript#custom-app). +If you’re using TypeScript, take a look at [our TypeScript documentation](/docs/basic-features/typescript.md#custom-app). ## Related diff --git a/docs/advanced-features/custom-document.md b/docs/advanced-features/custom-document.md index f9e60d9797883..2c75cca86b376 100644 --- a/docs/advanced-features/custom-document.md +++ b/docs/advanced-features/custom-document.md @@ -6,8 +6,6 @@ description: Extend the default document markup added by Next.js. A custom `Document` is commonly used to augment your application's `` and `` tags. This is necessary because Next.js pages skip the definition of the surrounding document's markup. -A custom `Document` can also include `getInitialProps` for expressing asynchronous server-rendering data requirements. - To override the default `Document`, create the file `./pages/_document.js` and extend the `Document` class as shown below: ```jsx @@ -35,6 +33,8 @@ class MyDocument extends Document { export default MyDocument ``` +> The code above is the default `Document` added by Next.js. Feel free to remove the `getInitialProps` or `render` function from `MyDocument` if you don't need to change them. + ``, ``, `
` and `` are required for the page to be properly rendered. Custom attributes are allowed as props, like `lang`: @@ -43,17 +43,18 @@ Custom attributes are allowed as props, like `lang`: ``` +The `` component used here is not the same one from [`next/head`](/docs/api-reference/next/head.md). The `` component used here should only be used for any `` code that is common for all pages. For all other cases, such as `` tags, we recommend using [`next/head`](/docs/api-reference/next/head.md) in your pages or components. + The `ctx` object is equivalent to the one received in [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md#context-object), with one addition: - `renderPage`: `Function` - a callback that runs the actual React rendering logic (synchronously). It's useful to decorate this function in order to support server-rendering wrappers like Aphrodite's [`renderStatic`](https://github.com/Khan/aphrodite#server-side-rendering) ## Caveats -- `Document` is only rendered in the server, event handlers like `onClick` won't work -- React components outside of `<Main />` will not be initialized by the browser. Do _not_ add application logic here. If you need shared components in all your pages (like a menu or a toolbar), take a look at the [`App`](/docs/advanced-features/custom-app.md) component instead -- `Document`'s `getInitialProps` function is not called during client-side transitions, nor when a page is [statically optimized](/docs/advanced-features/automatic-static-optimization.md) -- Make sure to check if `ctx.req` / `ctx.res` are defined in `getInitialProps`. Those variables will be `undefined` when a page is being statically exported by [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) or by [`next export`](/docs/advanced-features/static-html-export.md) -- Common errors include adding a `<title>` in the `<Head />` tag or using `styled-jsx`. These should be avoided in `pages/_document.js` as they lead to unexpected behavior +- `Document` is only rendered in the server, event handlers like `onClick` won't work. +- React components outside of `<Main />` will not be initialized by the browser. Do _not_ add application logic here or custom CSS (like `styled-jsx`). If you need shared components in all your pages (like a menu or a toolbar), take a look at the [`App`](/docs/advanced-features/custom-app.md) component instead. +- `Document`'s `getInitialProps` function is not called during client-side transitions, nor when a page is [statically optimized](/docs/advanced-features/automatic-static-optimization.md). +- `Document` currently does not support Next.js [Data Fetching methods](/docs/basic-features/data-fetching.md) like [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering). ## Customizing `renderPage` @@ -85,3 +86,21 @@ class MyDocument extends Document { export default MyDocument ``` + +## TypeScript + +You can use the built-in `DocumentContext` type and change the file name to `./pages/_document.tsx` like so: + +```tsx +import Document, { DocumentContext } from 'next/document' + +class MyDocument extends Document { + static async getInitialProps(ctx: DocumentContext) { + const initialProps = await Document.getInitialProps(ctx) + + return initialProps + } +} + +export default MyDocument +``` diff --git a/docs/advanced-features/custom-error-page.md b/docs/advanced-features/custom-error-page.md index f32c7928d1c58..6dfa999892be1 100644 --- a/docs/advanced-features/custom-error-page.md +++ b/docs/advanced-features/custom-error-page.md @@ -2,6 +2,8 @@ description: Override and extend the built-in Error page to handle custom errors. --- +# Custom Error Page + ## 404 Page A 404 page may be accessed very often. Server-rendering an error page for every visit increases the load of the Next.js server. This can result in increased costs and slow experiences. @@ -19,11 +21,26 @@ export default function Custom404() { } ``` +> **Note**: You can use [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) inside this page if you need to fetch data at build time. + ## 500 Page -By default Next.js provides a 500 error page that matches the default 404 page’s style. This page is not statically optimized as it allows server-side errors to be reported. This is why 404 and 500 (other errors) are separated. +Server-rendering an error page for every visit adds complexity to responding to errors. To help users get responses to errors as fast as possible, Next.js provides a static 500 page by default without having to add any additional files. + +### Customizing The 500 Page + +To customize the 500 page you can create a `pages/500.js` file. This file is statically generated at build time. + +```jsx +// pages/500.js +export default function Custom500() { + return <h1>500 - Server-side error occurred</h1> +} +``` -### Customizing The Error Page +> **Note**: You can use [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) inside this page if you need to fetch data at build time. + +### More Advanced Error Page Customizing 500 errors are handled both client-side and server-side by the `Error` component. If you wish to override it, define the file `pages/_error.js` and add the following code: @@ -75,3 +92,5 @@ export default function Page({ errorCode, stars }) { ``` The `Error` component also takes `title` as a property if you want to pass in a text message along with a `statusCode`. + +If you have a custom `Error` component be sure to import that one instead. `next/error` exports the default component used by Next.js. diff --git a/docs/advanced-features/custom-server.md b/docs/advanced-features/custom-server.md index 928fd5098aa51..f6d95c59e7b2d 100644 --- a/docs/advanced-features/custom-server.md +++ b/docs/advanced-features/custom-server.md @@ -15,9 +15,11 @@ description: Start a Next.js app programmatically using a custom server. </ul> </details> -Typically you start your next server with `next start`. It's possible, however, to start a server 100% programmatically in order to use custom route patterns. +By default, Next.js includes its own server with `next start`. If you have an existing backend, you can still use it with Next.js (this is not a custom server). A custom Next.js server allows you to start a server 100% programmatically in order to use custom server patterns. Most of the time, you will not need this – but it's available for complete customization. -> Before deciding to use a custom server please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like **serverless functions** and **[Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md).** +> **Note:** A custom server **can not** be deployed on [Vercel](https://vercel.com/solutions/nextjs). + +> Before deciding to use a custom server, please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like **serverless functions** and **[Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md).** Take a look at the following example of a custom server: @@ -96,4 +98,4 @@ module.exports = { > Note that `useFileSystemPublicRoutes` disables filename routes from SSR; client-side routing may still access those paths. When using this option, you should guard against navigation to routes you do not want programmatically. -> You may also wish to configure the client-side Router to disallow client-side redirects to filename routes; for that refer to [`Router.beforePopState`](/docs/api-reference/next/router.md#router.beforePopState). +> You may also wish to configure the client-side router to disallow client-side redirects to filename routes; for that refer to [`router.beforePopState`](/docs/api-reference/next/router.md#router.beforePopState). diff --git a/docs/advanced-features/customizing-babel-config.md b/docs/advanced-features/customizing-babel-config.md index c0c4db96aa9bd..aee52289f1589 100644 --- a/docs/advanced-features/customizing-babel-config.md +++ b/docs/advanced-features/customizing-babel-config.md @@ -11,9 +11,9 @@ description: Extend the babel preset added by Next.js with your own configs. </ul> </details> -Next.js includes the `next/babel` preset to your app, it includes everything needed to compile React applications and server-side code. But if you want to extend the default Babel configs, it's also possible. +Next.js includes the `next/babel` preset to your app, which includes everything needed to compile React applications and server-side code. But if you want to extend the default Babel configs, it's also possible. -To start, you only need to define a `.babelrc` file at the top of your app, if such file is found, we're going to consider it the _source of truth_, therefore it needs to define what Next.js needs as well, which is the `next/babel` preset. +To start, you only need to define a `.babelrc` file (or `babel.config.js`) at the top of your app. If such a file is found, it will be considered as the _source of truth_, and therefore it needs to define what Next.js needs as well, which is the `next/babel` preset. Here's an example `.babelrc` file: @@ -24,17 +24,18 @@ Here's an example `.babelrc` file: } ``` -The `next/babel` presets includes: +You can [take a look at this file](https://github.com/vercel/next.js/blob/canary/packages/next/build/babel/preset.ts) to learn about the presets included by `next/babel`. -- preset-env -- preset-react -- preset-typescript -- plugin-proposal-class-properties -- plugin-proposal-object-rest-spread -- plugin-transform-runtime -- styled-jsx +To add presets/plugins **without configuring them**, you can do it this way: -To configure these presets/plugins, **do not** add them to `presets` or `plugins` in your custom `.babelrc`. Instead, configure them on the `next/babel` preset, like so: +```json +{ + "presets": ["next/babel"], + "plugins": ["@babel/plugin-proposal-do-expressions"] +} +``` + +To add presets/plugins **with custom configuration**, do it on the `next/babel` preset like so: ```json { diff --git a/docs/advanced-features/customizing-postcss-config.md b/docs/advanced-features/customizing-postcss-config.md index be56de56eb2b9..b89b4817f11c2 100644 --- a/docs/advanced-features/customizing-postcss-config.md +++ b/docs/advanced-features/customizing-postcss-config.md @@ -13,7 +13,7 @@ description: Extend the PostCSS config and plugins added by Next.js with your ow ## Default Behavior -Next.js compiles CSS for its [built-in CSS support](/docs/basic-features/built-in-css-support) using PostCSS. +Next.js compiles CSS for its [built-in CSS support](/docs/basic-features/built-in-css-support.md) using PostCSS. Out of the box, with no configuration, Next.js compiles CSS with the following transformations: @@ -26,13 +26,48 @@ Out of the box, with no configuration, Next.js compiles CSS with the following t - [Gap Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/gap) - [Media Query Ranges](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Syntax_improvements_in_Level_4) -By default, [Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/var) (CSS variables) are **not compiled** for IE11 support. +By default, [CSS Grid](https://www.w3.org/TR/css-grid-1/) and [Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/var) (CSS variables) are **not compiled** for IE11 support. + +To compile [CSS Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/grid) for IE11, you can place the following comment at the top of your CSS file: + +```css +/* autoprefixer grid: autoplace */ +``` + +You can also enable IE11 support for [CSS Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/grid) +in your entire project by configuring autoprefixer with the configuration shown below (collapsed). +See ["Customizing Plugins"](#customizing-plugins) below for more information. + +<details> +<summary><strong>Click to view the configuration to enable CSS Grid Layout</strong></summary> + +```json +{ + "plugins": [ + "postcss-flexbugs-fixes", + [ + "postcss-preset-env", + { + "autoprefixer": { + "flexbox": "no-2009", + "grid": "autoplace" + }, + "stage": 3, + "features": { + "custom-properties": false + } + } + ] + ] +} +``` + +</details> +<br/> CSS variables are not compiled because it is [not possible to safely do so](https://github.com/MadLittleMods/postcss-css-variables#caveats). If you must use variables, consider using something like [Sass variables](https://sass-lang.com/documentation/variables) which are compiled away by [Sass](https://sass-lang.com/). -> **Note**: To support [Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/grid), you need to enable `grid: "autoplace"` for Autoprefixer. See ["Customizing Plugins"](#customizing-plugins) below. - ## Customizing Target Browsers Next.js allows you to configure the target browsers (for [Autoprefixer](https://github.com/postcss/autoprefixer) and compiled css features) through [Browserslist](https://github.com/browserslist/browserslist). @@ -51,13 +86,13 @@ You can use the [browserl.ist](https://browserl.ist/?q=%3E0.3%25%2C+not+ie+11%2C No configuration is needed to support CSS Modules. To enable CSS Modules for a file, rename the file to have the extension `.module.css`. -You can learn more about [Next.js' CSS Module support here](/docs/basic-features/built-in-css-support). +You can learn more about [Next.js' CSS Module support here](/docs/basic-features/built-in-css-support.md). ## Customizing Plugins > **Warning**: When you define a custom PostCSS configuration file, Next.js **completely disables** the [default behavior](#default-behavior). > Be sure to manually configure all the features you need compiled, including [Autoprefixer](https://github.com/postcss/autoprefixer). -> You also need to install any plugins included in your custom configuration manually, i.e. `npm install postcss-flexbugs-fixes`. +> You also need to install any plugins included in your custom configuration manually, i.e. `npm install postcss-flexbugs-fixes postcss-preset-env`. To customize the PostCSS configuration, create a `postcss.config.json` file in the root of your project. diff --git a/docs/advanced-features/debugging.md b/docs/advanced-features/debugging.md index 05e4452360fc8..d400d334367db 100644 --- a/docs/advanced-features/debugging.md +++ b/docs/advanced-features/debugging.md @@ -20,7 +20,7 @@ First, start Next.js with the inspect flag: NODE_OPTIONS='--inspect' next dev ``` -If you're using `npm run dev` or `yarn dev` (See: [Getting Started](/docs/getting-started)) then you should update the `dev` script on your `package.json`: +If you're using `npm run dev` or `yarn dev` (See: [Getting Started](/docs/getting-started.md)) then you should update the `dev` script on your `package.json`: ```json "dev": "NODE_OPTIONS='--inspect' next dev" @@ -40,7 +40,7 @@ ready - started server on http://localhost:3000 ### Using Chrome DevTools -Once you open a new tab in Google Chrome and go to `chrome://inspect`, you should see your Next.js application inside the "Remote Target" section. Now click "inspect" to open a screen that will be your debugging environement from now on. +Once you open a new tab in Google Chrome and go to `chrome://inspect`, you should see your Next.js application inside the "Remote Target" section. Now click "inspect" to open a screen that will be your debugging environment from now on. ### Using the Debugger in Visual Studio Code @@ -55,7 +55,7 @@ Create a file named `.vscode/launch.json` at the root of your project with this { "type": "node", "request": "attach", - "name": "Launch Program", + "name": "Attach to application", "skipFiles": ["<node_internals>/**"], "port": 9229 } @@ -69,9 +69,9 @@ Now hit <kdb>F5</kbd> or select **Debug: Start Debugging** from the Command Pale Now you can use the [`debugger`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger) statement to pause your backend or frontend code anytime you want to observe and debug your code more precisely. -If you trigger the underlying code by refreshing the current page, clicking on a page link or fetching an API route, your code will be paused and the debugger window will pop up. +If you trigger the underlying code by refreshing the current page, clicking on a page link or fetching an API route, your code will be paused and the debugger window will appear. -To learn more on how to use a JavaScript debugger, take a look the following documentation: +To learn more on how to use a JavaScript debugger, take a look at the following documentation: - [VS Code Node.js debugging: Breakpoints](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_breakpoints) - [Get Started with Debugging JavaScript in Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/javascript) diff --git a/docs/advanced-features/dynamic-import.md b/docs/advanced-features/dynamic-import.md index b6ef8e5122e2d..fd92848067e99 100644 --- a/docs/advanced-features/dynamic-import.md +++ b/docs/advanced-features/dynamic-import.md @@ -4,17 +4,49 @@ description: Dynamically import JavaScript modules and React Components and spli # Dynamic Import -<details> +<details open> <summary><b>Examples</b></summary> <ul> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-dynamic-import">Dynamic Import</a></li> </ul> </details> -Next.js supports ES2020 [dynamic `import()`](https://github.com/tc39/proposal-dynamic-import) for JavaScript. With it you can import JavaScript modules (inc. React Components) dynamically and work with them. They also work with SSR. +Next.js supports ES2020 [dynamic `import()`](https://github.com/tc39/proposal-dynamic-import) for JavaScript. With it you can import JavaScript modules dynamically and work with them. They also work with SSR. + +In the following example, we implement fuzzy search using `fuse.js` and only load the module dynamically in the browser after the user types in the search input: + +```jsx +import { useState } from 'react' + +const names = ['Tim', 'Joe', 'Bel', 'Max', 'Lee'] + +export default function Page() { + const [results, setResults] = useState() + + return ( + <div> + <input + type="text" + placeholder="Search" + onChange={async (e) => { + const { value } = e.currentTarget + // Dynamically load fuse.js + const Fuse = (await import('fuse.js')).default + const fuse = new Fuse(names) + + setResults(fuse.search(value)) + }} + /> + <pre>Results: {JSON.stringify(results, null, 2)}</pre> + </div> + ) +} +``` You can think of dynamic imports as another way to split your code into manageable chunks. +React components can also be imported using dynamic imports, but in this case we use it in conjunction with `next/dynamic` to make sure it works like any other React Component. Check out the sections below for more details on how it works. + ## Basic usage In the following example, the module `../components/hello` will be dynamically loaded by the page: @@ -39,6 +71,8 @@ export default Home `DynamicComponent` will be the default component returned by `../components/hello`. It works like a regular React Component, and you can pass props to it as you normally would. +> **Note**: In `import('path/to/component')`, the path must be explicitly written. It can't be a template string nor a variable. Furthermore the `import()` has to be inside the `dynamic()` call for Next.js to be able to match webpack bundles / module ids to the specific `dynamic()` call and preload them before rendering. `dynamic()` can't be used inside of React rendering as it needs to be marked in the top level of the module for preloading to work, similar to `React.lazy`. + ## With named exports If the dynamic component is not the default export, you can use a named export too. Consider the module `../components/hello.js` which has a named export `Hello`: @@ -122,3 +156,25 @@ function Home() { export default Home ``` + +## With suspense + +Option `suspense` allows you to lazy-load a component, similar to `React.lazy` and `<Suspense>` with React 18. Note that it only works on client-side or server-side with `fallback`. Full SSR support in concurrent mode is still a work-in-progress. + +```jsx +import dynamic from 'next/dynamic' + +const DynamicLazyComponent = dynamic(() => import('../components/hello4'), { + suspense: true, +}) + +function Home() { + return ( + <div> + <Suspense fallback={`loading`}> + <DynamicLazyComponent /> + </Suspense> + </div> + ) +} +``` diff --git a/docs/advanced-features/i18n-routing.md b/docs/advanced-features/i18n-routing.md new file mode 100644 index 0000000000000..219a9956e5aa1 --- /dev/null +++ b/docs/advanced-features/i18n-routing.md @@ -0,0 +1,297 @@ +--- +description: Next.js has built-in support for internationalized routing and language detection. Learn more here. +--- + +# Internationalized Routing + +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/i18n-routing">i18n routing</a></li> + </ul> +</details> + +Next.js has built-in support for internationalized ([i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization#Naming)) routing since `v10.0.0`. You can provide a list of locales, the default locale, and domain-specific locales and Next.js will automatically handle the routing. + +The i18n routing support is currently meant to complement existing i18n library solutions like [`react-intl`](https://formatjs.io/docs/getting-started/installation), [`react-i18next`](https://react.i18next.com/), [`lingui`](https://lingui.js.org/), [`rosetta`](https://github.com/lukeed/rosetta), [`next-intl`](https://github.com/amannn/next-intl) and others by streamlining the routes and locale parsing. + +## Getting started + +To get started, add the `i18n` config to your `next.config.js` file. + +Locales are [UTS Locale Identifiers](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers), a standardized format for defining locales. + +Generally a Locale Identifier is made up of a language, region, and script separated by a dash: `language-region-script`. The region and script are optional. An example: + +- `en-US` - English as spoken in the United States +- `nl-NL` - Dutch as spoken in the Netherlands +- `nl` - Dutch, no specific region + +```js +// next.config.js +module.exports = { + i18n: { + // These are all the locales you want to support in + // your application + locales: ['en-US', 'fr', 'nl-NL'], + // This is the default locale you want to be used when visiting + // a non-locale prefixed path e.g. `/hello` + defaultLocale: 'en-US', + // This is a list of locale domains and the default locale they + // should handle (these are only required when setting up domain routing) + // Note: subdomains must be included in the domain value to be matched e.g. "fr.example.com". + domains: [ + { + domain: 'example.com', + defaultLocale: 'en-US', + }, + { + domain: 'example.nl', + defaultLocale: 'nl-NL', + }, + { + domain: 'example.fr', + defaultLocale: 'fr', + // an optional http field can also be used to test + // locale domains locally with http instead of https + http: true, + }, + ], + }, +} +``` + +## Locale Strategies + +There are two locale handling strategies: Sub-path Routing and Domain Routing. + +### Sub-path Routing + +Sub-path Routing puts the locale in the url path. + +```js +// next.config.js +module.exports = { + i18n: { + locales: ['en-US', 'fr', 'nl-NL'], + defaultLocale: 'en-US', + }, +} +``` + +With the above configuration `en-US`, `fr`, and `nl-NL` will be available to be routed to, and `en-US` is the default locale. If you have a `pages/blog.js` the following urls would be available: + +- `/blog` +- `/fr/blog` +- `/nl-nl/blog` + +The default locale does not have a prefix. + +### Domain Routing + +By using domain routing you can configure locales to be served from different domains: + +```js +// next.config.js +module.exports = { + i18n: { + locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'], + defaultLocale: 'en-US', + + domains: [ + { + domain: 'example.com', + defaultLocale: 'en-US', + }, + { + domain: 'example.fr', + defaultLocale: 'fr', + }, + { + domain: 'example.nl', + defaultLocale: 'nl-NL', + // specify other locales that should be redirected + // to this domain + locales: ['nl-BE'], + }, + ], + }, +} +``` + +For example if you have `pages/blog.js` the following urls will be available: + +- `example.com/blog` +- `example.fr/blog` +- `example.nl/blog` +- `example.nl/nl-BE/blog` + +## Automatic Locale Detection + +When a user visits the application root (generally `/`), Next.js will try to automatically detect which locale the user prefers based on the [`Accept-Language`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language) header and the current domain. + +If a locale other than the default locale is detected, the user will be redirected to either: + +- **When using Sub-path Routing:** The locale prefixed path +- **When using Domain Routing:** The domain with that locale specified as the default + +When using Domain Routing, if a user with the `Accept-Language` header `fr;q=0.9` visits `example.com`, they will be redirected to `example.fr` since that domain handles the `fr` locale by default. + +When using Sub-path Routing, the user would be redirected to `/fr`. + +### Disabling Automatic Locale Detection + +The automatic locale detection can be disabled with: + +```js +// next.config.js +module.exports = { + i18n: { + localeDetection: false, + }, +} +``` + +When `localeDetection` is set to `false` Next.js will no longer automatically redirect based on the user's preferred locale and will only provide locale information detected from either the locale based domain or locale path as described above. + +## Accessing the locale information + +You can access the locale information via the Next.js router. For example, using the [`useRouter()`](/docs/api-reference/next/router.md#userouter) hook the following properties are available: + +- `locale` contains the currently active locale. +- `locales` contains all configured locales. +- `defaultLocale` contains the configured default locale. + +When [pre-rendering](/docs/basic-features/pages.md#static-generation-recommended) pages with `getStaticProps` or `getServerSideProps`, the locale information is provided in [the context](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) provided to the function. + +When leveraging `getStaticPaths`, the configured locales are provided in the context parameter of the function under `locales` and the configured defaultLocale under `defaultLocale`. + +## Transition between locales + +You can use `next/link` or `next/router` to transition between locales. + +For `next/link`, a `locale` prop can be provided to transition to a different locale from the currently active one. If no `locale` prop is provided, the currently active `locale` is used during client-transitions. For example: + +```jsx +import Link from 'next/link' + +export default function IndexPage(props) { + return ( + <Link href="/another" locale="fr"> + <a>To /fr/another</a> + </Link> + ) +} +``` + +When using the `next/router` methods directly, you can specify the `locale` that should be used via the transition options. For example: + +```jsx +import { useRouter } from 'next/router' + +export default function IndexPage(props) { + const router = useRouter() + + return ( + <div + onClick={() => { + router.push('/another', '/another', { locale: 'fr' }) + }} + > + to /fr/another + </div> + ) +} +``` + +If you have a `href` that already includes the locale you can opt-out of automatically handling the locale prefixing: + +```jsx +import Link from 'next/link' + +export default function IndexPage(props) { + return ( + <Link href="/fr/another" locale={false}> + <a>To /fr/another</a> + </Link> + ) +} +``` + +## Leveraging the NEXT_LOCALE cookie + +Next.js supports overriding the accept-language header with a `NEXT_LOCALE=the-locale` cookie. This cookie can be set using a language switcher and then when a user comes back to the site it will leverage the locale specified in the cookie when redirecting from `/` to the correct locale location. + +For example, if a user prefers the locale `fr` in their accept-language header but a `NEXT_LOCALE=en` cookie is set the `en` locale when visiting `/` the user will be redirected to the `en` locale location until the cookie is removed or expired. + +## Search Engine Optimization + +Since Next.js knows what language the user is visiting it will automatically add the `lang` attribute to the `<html>` tag. + +Next.js doesn't know about variants of a page so it's up to you to add the `hreflang` meta tags using [`next/head`](/docs/api-reference/next/head.md). You can learn more about `hreflang` in the [Google Webmasters documentation](https://support.google.com/webmasters/answer/189077). + +## How does this work with Static Generation? + +> Note that Internationalized Routing does not integrate with [`next export`](/docs/advanced-features/static-html-export.md) as `next export` does not leverage the Next.js routing layer. Hybrid Next.js applications that do not use `next export` are fully supported. + +### Dynamic Routes and `getStaticProps` Pages + +For pages using `getStaticProps` with [Dynamic Routes](/docs/routing/dynamic-routes.md), all locale variants of the page desired to be prerendered need to be returned from [`getStaticPaths`](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation). Along with the `params` object returned for `paths`, you can also return a `locale` field specifying which locale you want to render. For example: + +```js +// pages/blog/[slug].js +export const getStaticPaths = ({ locales }) => { + return { + paths: [ + // if no `locale` is provided only the defaultLocale will be generated + { params: { slug: 'post-1' }, locale: 'en-US' }, + { params: { slug: 'post-1' }, locale: 'fr' }, + ], + fallback: true, + } +} +``` + +For [Automatically Statically Optimized](/docs/advanced-features/automatic-static-optimization.md) and non-dynamic `getStaticProps` pages, **a version of the page will be generated for each locale**. This is important to consider because it can increase build times depending on how many locales are configured inside `getStaticProps`. + +For example, if you have 50 locales configured with 10 non-dynamic pages using `getStaticProps`, this means `getStaticProps` will be called 500 times. 50 versions of the 10 pages will be generated during each build. + +To decrease the build time of dynamic pages with `getStaticProps`, use a [`fallback` mode](https://nextjs.org/docs/basic-features/data-fetching#fallback-true). This allows you to return only the most popular paths and locales from `getStaticPaths` for prerendering during the build. Then, Next.js will build the remaining pages at runtime as they are requested. + +### Automatically Statically Optimized Pages + +For pages that are [automatically statically optimized](/docs/advanced-features/automatic-static-optimization.md), a version of the page will be generated for each locale. + +### Non-dynamic getStaticProps Pages + +For non-dynamic `getStaticProps` pages, a version is generated for each locale like above. `getStaticProps` is called with each `locale` that is being rendered. If you would like to opt-out of a certain locale from being pre-rendered, you can return `notFound: true` from `getStaticProps` and this variant of the page will not be generated. + +```js +export async function getStaticProps({ locale }) { + // Call an external API endpoint to get posts. + // You can use any data fetching library + const res = await fetch(`https://.../posts?locale=${locale}`) + const posts = await res.json() + + if (posts.length === 0) { + return { + notFound: true, + } + } + + // By returning { props: posts }, the Blog component + // will receive `posts` as a prop at build time + return { + props: { + posts, + }, + } +} +``` + +## Limits for the i18n config + +- `locales`: 100 total locales +- `domains`: 100 total locale domain items + +> **Note:** These limits have been added initially to prevent potential [performance issues at build time](#dynamic-routes-and-getStaticProps-pages). We are continuing to evaluate if these limits are sufficient. diff --git a/docs/advanced-features/measuring-performance.md b/docs/advanced-features/measuring-performance.md index b84dab4876664..789d94bb8759c 100644 --- a/docs/advanced-features/measuring-performance.md +++ b/docs/advanced-features/measuring-performance.md @@ -1,14 +1,19 @@ --- -description: Measure and track page performance using Next.js's build-in performance relayer +description: Measure and track page performance using Next.js Analytics --- # Measuring performance -Next.js has a built-in relayer that allows you to analyze and measure the performance of +[Next.js Analytics](https://nextjs.org/analytics) allows you to analyze and measure the performance of pages using different metrics. -To measure any of the supported metrics, you will need to create a [custom -App](/docs/advanced-features/custom-app.md) component and define a `reportWebVitals` function: +You can start collecting your [Real Experience Score](https://vercel.com/docs/analytics#metrics) with zero-configuration on [Vercel deployments](https://vercel.com/docs/analytics). There's also support for Analytics if you're [self-hosting](https://vercel.com/docs/analytics#self-hosted). + +The rest of this documentation describes the built-in relayer Next.js Analytics uses. + +## Build Your Own + +First, you will need to create a [custom App](/docs/advanced-features/custom-app.md) component and define a `reportWebVitals` function: ```js // pages/_app.js @@ -30,8 +35,8 @@ The `metric` object returned to the function consists of a number of properties: - `id`: Unique identifier for the metric in the context of the current page load - `name`: Metric name -- `startTime`: First recorded timestamp of the performance entry (if applicable) -- `value`: Value, or duration, of performance entry +- `startTime`: First recorded timestamp of the performance entry in [milliseconds](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp) (if applicable) +- `value`: Value, or duration in [milliseconds](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp), of the performance entry - `label`: Type of metric (`web-vital` or `custom`) There are two types of metrics that are tracked: @@ -158,15 +163,36 @@ export function reportWebVitals(metric) { > > ```js > export function reportWebVitals({ id, name, label, value }) { -> ga('send', 'event', { -> eventCategory: +> // Use `window.gtag` if you initialized Google Analytics as this example: +> // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics/pages/_document.js +> window.gtag('event', name, { +> event_category: > label === 'web-vital' ? 'Web Vitals' : 'Next.js custom metric', -> eventAction: name, -> eventValue: Math.round(name === 'CLS' ? value * 1000 : value), // values must be integers -> eventLabel: id, // id unique to current page load -> nonInteraction: true, // avoids affecting bounce rate. +> value: Math.round(name === 'CLS' ? value * 1000 : value), // values must be integers +> event_label: id, // id unique to current page load +> non_interaction: true, // avoids affecting bounce rate. > }) > } > ``` > -> Read more about sending results to Google Analytics [here](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics). +> Read more about [sending results to Google Analytics](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics). + +## TypeScript + +If you are using TypeScript, you can use the built-in type `NextWebVitalsMetric`: + +```ts +// pages/_app.tsx + +import type { AppProps, NextWebVitalsMetric } from 'next/app' + +function MyApp({ Component, pageProps }: AppProps) { + return <Component {...pageProps} /> +} + +export function reportWebVitals(metric: NextWebVitalsMetric) { + console.log(metric) +} + +export default MyApp +``` diff --git a/docs/advanced-features/module-path-aliases.md b/docs/advanced-features/module-path-aliases.md index 0d88026abdcff..8015f82667cd0 100644 --- a/docs/advanced-features/module-path-aliases.md +++ b/docs/advanced-features/module-path-aliases.md @@ -4,11 +4,20 @@ description: Configure module path aliases that allow you to remap certain impor # Absolute Imports and Module path aliases -Next.js automatically supports the `tsconfig.json` and `jsconfig.json` `"paths"` and `"baseUrl"` options. +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-absolute-imports">Absolute Imports</a></li> + </ul> +</details> + +Next.js automatically supports the `tsconfig.json` and `jsconfig.json` `"paths"` and `"baseUrl"` options since [Next.js 9.4](https://nextjs.org/blog/next-9-4). > Note: `jsconfig.json` can be used when you don't use TypeScript -These option allow you to configure module aliases, for example a common pattern is aliasing certain directories to use absolute paths. +> Note: you need to restart dev server to reflect modifications done in `tsconfig.json` / `jsconfig.json` + +These options allow you to configure module aliases, for example a common pattern is aliasing certain directories to use absolute paths. One useful feature of these options is that they integrate automatically into certain editors, for example vscode. diff --git a/docs/advanced-features/multi-zones.md b/docs/advanced-features/multi-zones.md index 722f185870674..7d288941b0cb9 100644 --- a/docs/advanced-features/multi-zones.md +++ b/docs/advanced-features/multi-zones.md @@ -1,9 +1,9 @@ # Multi Zones -<details> +<details open> <summary><b>Examples</b></summary> <ul> - <li><a href="/examples/with-zones">With Zones</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-zones">With Zones</a></li> </ul> </details> @@ -18,30 +18,13 @@ With multi zones support, you can merge both these apps into a single one allowi ## How to define a zone -There are no special zones related APIs. You only need to do following: +There are no zone related APIs. You only need to do the following: - Make sure to keep only the pages you need in your app, meaning that an app can't have pages from another app, if app `A` has `/blog` then app `B` shouldn't have it too. -- Make sure to add an [assetPrefix](/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md) to avoid conflicts with static files. +- Make sure to configure a [basePath](/docs/api-reference/next.config.js/basepath.md) to avoid conflicts with pages and static files. ## How to merge zones -You can merge zones using any HTTP proxy. - -For [Vercel](https://vercel.com/now), you can use a single `now.json` to deploy both apps. It allows you to define routing routes for multiple apps like below: - -```json -{ - "version": 2, - "builds": [ - { "src": "blog/package.json", "use": "@now/next" }, - { "src": "home/package.json", "use": "@now/next" } - ], - "routes": [ - { "src": "/blog/_next(.*)", "dest": "blog/_next$1" }, - { "src": "/blog(.*)", "dest": "blog/blog$1" }, - { "src": "(.*)", "dest": "home$1" } - ] -} -``` - -You can also configure a proxy server to route using a set of routes like the ones above, e.g deploy the blog app to `https://blog.example.com` and the home app to `https://home.example.com` and then add a proxy server for both apps in `https://example.com`. +You can merge zones using [Rewrites](/docs/api-reference/next.config.js/rewrites.md) in one of the apps or any HTTP proxy. + +For [Vercel](https://vercel.com/), you can use a [monorepo](https://vercel.com/blog/monorepos) to deploy both apps. Check the [Monorepos blog post](https://vercel.com/blog/monorepos) for more details on how it works and our [`with-zones` example](https://github.com/vercel/next.js/tree/canary/examples/with-zones) for a detailed guide using multiple Next.js applications. diff --git a/docs/advanced-features/preview-mode.md b/docs/advanced-features/preview-mode.md index 9a5d195a8a6f2..98d1ae6698cb8 100644 --- a/docs/advanced-features/preview-mode.md +++ b/docs/advanced-features/preview-mode.md @@ -9,11 +9,20 @@ description: Next.js has the preview mode for statically generated pages. You ca <details open> <summary><b>Examples</b></summary> <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.now.sh/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-wordpress">WordPress Example</a> (<a href="https://next-blog-wordpress.vercel.app">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-strapi">Strapi Example</a> (<a href="https://next-blog-strapi.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prepr">Prepr Example</a> (<a href="https://next-blog-prepr.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-agilitycms">Agility CMS Example</a> (<a href="https://next-blog-agilitycms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic">Cosmic Example</a> (<a href="https://next-blog-cosmic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-buttercms">ButterCMS Example</a> (<a href="https://next-blog-buttercms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app//">Demo</a>)</li> </ul> </details> @@ -21,7 +30,7 @@ In the [Pages documentation](/docs/basic-features/pages.md) and the [Data Fetchi Static Generation is useful when your pages fetch data from a headless CMS. However, it’s not ideal when you’re writing a draft on your headless CMS and want to **preview** the draft immediately on your page. You’d want Next.js to render these pages at **request time** instead of build time and fetch the draft content instead of the published content. You’d want Next.js to bypass Static Generation only for this specific case. -Next.js has the feature called **Preview Mode** which solves this problem. Here’s an instruction on how to use it. +Next.js has a feature called **Preview Mode** which solves this problem. Here are instructions on how to use it. ## Step 1. Create and access a preview API route @@ -32,7 +41,7 @@ First, create a **preview API route**. It can have any name - e.g. `pages/api/pr In this API route, you need to call `setPreviewData` on the response object. The argument for `setPreviewData` should be an object, and this can be used by `getStaticProps` (more on this later). For now, we’ll use `{}`. ```js -export default (req, res) => { +export default function handler(req, res) { // ... res.setPreviewData({}) // ... @@ -47,7 +56,7 @@ You can test this manually by creating an API route like below and accessing it // A simple example for testing it manually from your browser. // If this is located at pages/api/preview.js, then // open /api/preview from your browser. -export default (req, res) => { +export default function handler(req, res) { res.setPreviewData({}) res.end('Preview mode enabled') } @@ -104,8 +113,7 @@ export default async (req, res) => { // Redirect to the path from the fetched post // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities - res.writeHead(307, { Location: post.slug }) - res.end() + res.redirect(post.slug) } ``` @@ -169,7 +177,7 @@ By default, no expiration date is set for the preview mode cookies, so the previ To clear the preview cookies manually, you can create an API route which calls `clearPreviewData` and then access this API route. ```js -export default (req, res) => { +export default function handler(req, res) { // Clears the preview mode cookies. // This function accepts no arguments. res.clearPreviewData() @@ -197,9 +205,24 @@ You can pass an object to `setPreviewData` and have it be available in `getStati The preview mode works on `getServerSideProps` as well. It will also be available on the `context` object containing `preview` and `previewData`. +### Works with API Routes + +API Routes will have access to `preview` and `previewData` under the request object. For example: + +```js +export default function myApiRoute(req, res) { + const isPreview = req.preview + const previewData = req.previewData + // ... +} +``` + ### Unique per `next build` -The bypass cookie value and private key for encrypting the `previewData` changes when a `next build` is ran, this ensures that the bypass cookie can’t be guessed. +Both the bypass cookie value and the private key for encrypting the `previewData` change when `next build` is completed. +This ensures that the bypass cookie can’t be guessed. + +> **Note:** To test Preview Mode locally over HTTP your browser will need to allow third-party cookies and local storage access. ## Learn more diff --git a/docs/advanced-features/security-headers.md b/docs/advanced-features/security-headers.md new file mode 100644 index 0000000000000..c51718bdcb08e --- /dev/null +++ b/docs/advanced-features/security-headers.md @@ -0,0 +1,139 @@ +--- +description: Improve the security of your Next.js application by adding HTTP response headers. +--- + +# Security Headers + +To improve the security of your application, you can use [`headers`](/docs/api-reference/next.config.js/headers.md) in `next.config.js` to apply HTTP response headers to all routes in your application. + +```jsx +// next.config.js + +// You can choose which headers to add to the list +// after learning more below. +const securityHeaders = [] + +module.exports = { + async headers() { + return [ + { + // Apply these headers to all routes in your application. + source: '/(.*)', + headers: securityHeaders, + }, + ] + }, +} +``` + +## Options + +### [X-DNS-Prefetch-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) + +This header controls DNS prefetching, allowing browsers to proactively perform domain name resolution on external links, images, CSS, JavaScript, and more. This prefetching is performed in the background, so the [DNS](https://developer.mozilla.org/en-US/docs/Glossary/DNS) is more likely to be resolved by the time the referenced items are needed. This reduces latency when the user clicks a link. + +```jsx +{ + key: 'X-DNS-Prefetch-Control', + value: 'on' +} +``` + +### [Strict-Transport-Security](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) + +This header informs browsers it should only be accessed using HTTPS, instead of using HTTP. Using the configuration below, all present and future subdomains will use HTTPS for a `max-age` of 2 years. This blocks access to pages or subdomains that can only be served over HTTP. + +If you're deploying to [Vercel](https://vercel.com/docs/edge-network/headers#strict-transport-security), this header is not necessary as it's automatically added to all deployments. + +```jsx +{ + key: 'Strict-Transport-Security', + value: 'max-age=63072000; includeSubDomains; preload' +} +``` + +### [X-XSS-Protection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection) + +This header stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Although this protection is not necessary when sites implement a strong [`Content-Security-Policy`](#content-security-policy) disabling the use of inline JavaScript (`'unsafe-inline'`), it can still provide protection for older web browsers that don't support CSP. + +```jsx +{ + key: 'X-XSS-Protection', + value: '1; mode=block' +} +``` + +### [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) + +This header indicates whether the site should be allowed to be displayed within an `iframe`. This can prevent against clickjacking attacks. This header has been superseded by CSP's `frame-ancestors` option, which has better support in modern browsers. + +```jsx +{ + key: 'X-Frame-Options', + value: 'SAMEORIGIN' +} +``` + +### [Permissions-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy) + +This header allows you to control which features and APIs can be used in the browser. It was previously named `Feature-Policy`. You can view the full list of permission options [here](https://www.w3.org/TR/permissions-policy-1/). + +```jsx +{ + key: 'Permissions-Policy', + value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()' +} +``` + +### [X-Content-Type-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) + +This header prevents the browser from attempting to guess the type of content if the `Content-Type` header is not explicitly set. This can prevent XSS exploits for websites that allow users to upload and share files. For example, a user trying to download an image, but having it treated as a different `Content-Type` like an executable, which could be malicious. This header also applies to downloading browser extensions. The only valid value for this header is `nosniff`. + +```jsx +{ + key: 'X-Content-Type-Options', + value: 'nosniff' +} +``` + +### [Referrer-Policy](https://scotthelme.co.uk/a-new-security-header-referrer-policy/) + +This header controls how much information the browser includes when navigating from the current website (origin) to another. You can read about the different options [here](https://scotthelme.co.uk/a-new-security-header-referrer-policy/). + +```jsx +{ + key: 'Referrer-Policy', + value: 'origin-when-cross-origin' +} +``` + +### [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) + +This header helps prevent cross-site scripting (XSS), clickjacking and other code injection attacks. Content Security Policy (CSP) can specify allowed origins for content including scripts, stylesheets, images, fonts, objects, media (audio, video), iframes, and more. + +You can read about the many different CSP options [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). + +```jsx +{ + key: 'Content-Security-Policy', + value: // Your CSP Policy +} +``` + +### References + +- [MDN](https://developer.mozilla.org) +- [Varun Naik](https://blog.vnaik.com/posts/web-attacks.html) +- [Scott Helme](https://scotthelme.co.uk) +- [Mozilla Observatory](https://observatory.mozilla.org/) + +## Related + +For more information, we recommend the following sections: + +<div class="card"> + <a href="/docs/api-reference/next.config.js/headers.md"> + <b>Headers:</b> + <small>Add custom HTTP headers to your Next.js app.</small> + </a> +</div> diff --git a/docs/advanced-features/source-maps.md b/docs/advanced-features/source-maps.md new file mode 100644 index 0000000000000..aa07ea4cf5982 --- /dev/null +++ b/docs/advanced-features/source-maps.md @@ -0,0 +1,23 @@ +--- +description: Enables browser source map generation during the production build. +--- + +# Source Maps + +Source Maps are enabled by default during development. During production builds, they are disabled as generating source maps can significantly increase build times and memory usage while being generated. + +Next.js provides a configuration flag you can use to enable browser source map generation during the production build: + +```js +// next.config.js +module.exports = { + productionBrowserSourceMaps: true, +} +``` + +When the `productionBrowserSourceMaps` option is enabled, the source maps will be output in the same directory as the JavaScript files. Next.js will automatically serve these files when requested. + +## Caveats + +- Adding source maps can increase `next build` time +- Increases memory usage during `next build` diff --git a/docs/advanced-features/static-html-export.md b/docs/advanced-features/static-html-export.md index becfd255d69ed..c217b55d952ac 100644 --- a/docs/advanced-features/static-html-export.md +++ b/docs/advanced-features/static-html-export.md @@ -17,11 +17,11 @@ The exported app supports almost every feature of Next.js, including dynamic rou `next export` works by prerendering all pages to HTML. For [dynamic routes](/docs/routing/dynamic-routes.md), your page can export a [`getStaticPaths`](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation) function to let the exporter know which HTML pages to generate for that route. -> `next export` is intended for scenarios where **none** of your pages have server-side or incremental data requirements (though statically-rendered pages can still [fetch data on the client side](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side) just fine). +> `next export` is intended for scenarios where **none** of your pages have server-side or incremental data requirements (though statically-rendered pages can still [fetch data on the client side](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side)). > > If you're looking to make a hybrid site where only _some_ pages are prerendered to static HTML, Next.js already does that automatically for you! Read up on [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) for details. > -> `next export` also causes features like Incremental Static Generation and Regeneration to be disabled, as they require `next start` or a serverless deployment to function. +> `next export` also causes features like [Incremental Static Generation](/docs/basic-features/data-fetching.md#fallback-true) and [Regeneration](/docs/basic-features/data-fetching.md#incremental-static-regeneration) to be disabled, as they require [`next start`](/docs/api-reference/cli.md#production) or a serverless deployment to function. ## How to use it @@ -53,7 +53,9 @@ For more advanced scenarios, you can define a parameter called [`exportPathMap`] ## Deployment -You can read about deploying your Next.js application in the [deployment section](/docs/deployment.md). +By default, `next export` will generate an `out` directory, which can be served by any static hosting service or CDN. + +> We strongly recommend using [Vercel](https://vercel.com/) even if your Next.js app is fully static. [Vercel](https://vercel.com/) is optimized to make static Next.js apps blazingly fast. `next export` works with Zero Config deployments on Vercel. ## Caveats @@ -62,11 +64,12 @@ You can read about deploying your Next.js application in the [deployment section - `getInitialProps` cannot be used alongside `getStaticProps` or `getStaticPaths` on any given page. If you have dynamic routes, instead of using `getStaticPaths` you'll need to configure the [`exportPathMap`](/docs/api-reference/next.config.js/exportPathMap.md) parameter in your [`next.config.js`](/docs/api-reference/next.config.js/introduction.md) file to let the exporter know which HTML files it should output. - When `getInitialProps` is called during export, the `req` and `res` fields of its [`context`](/docs/api-reference/data-fetching/getInitialProps.md#context-object) parameter will be empty objects, since during export there is no server running. - `getInitialProps` **will be called on every client-side navigation**, if you'd like to only fetch data at build-time, switch to `getStaticProps`. - - `getInitialProps` cannot use Node.js-specific libraries or the file system like `getStaticProps` can. `getInitialProps` must fetch from an API. + - `getInitialProps` should fetch from an API and cannot use Node.js-specific libraries or the file system like `getStaticProps` can. - For static export, the `getStaticProps` API is always preferred over `getInitialProps`: it's recommended you convert your pages to use the `getStaticProps` if possible. + It's recommended to use and migrate towards `getStaticProps` over `getInitialProps` whenever possible. -- The `fallback: true` mode of `getStaticPaths` is not supported when using `next export`. -- You won't be able to render HTML dynamically when static exporting, as the HTML files are pre-build. Your application can be a hybrid of [Static Generation](/docs/basic-features/pages.md#static-generation) and [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering) when you don't use `next export`. You can learn more about it in the [pages section](/docs/basic-features/pages.md). +- The [`fallback: true`](/docs/basic-features/data-fetching.md#fallback-true) mode of `getStaticPaths` is not supported when using `next export`. - [API Routes](/docs/api-routes/introduction.md) are not supported by this method because they can't be prerendered to HTML. - [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) cannot be used within pages because the method requires a server. Consider using [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) instead. +- [Internationalized Routing](/docs/advanced-features/i18n-routing.md) is not supported as it requires Next.js' server-side routing. +- The [`next/image`](/docs/api-reference/next/image.md) component's default loader is not supported when using `next export`. However, other [loader](/docs/basic-features/image-optimization.md#loader) options will work. diff --git a/docs/api-reference/cli.md b/docs/api-reference/cli.md index badf0e9510376..b79edb5f0bbce 100644 --- a/docs/api-reference/cli.md +++ b/docs/api-reference/cli.md @@ -21,7 +21,7 @@ Usage $ next <command> Available commands - build, start, export, dev, telemetry + build, start, export, dev, lint, telemetry Options --version, -v Version number @@ -46,7 +46,23 @@ NODE_OPTIONS='--inspect' next - **Size** – The number of assets downloaded when navigating to the page client-side. The size for each route only includes its dependencies. - **First Load JS** – The number of assets downloaded when visiting the page from the server. The amount of JS shared by all is shown as a separate metric. -The first load is colored green, yellow, or red. Aim for green for performant applications. +The first load is indicated by green, yellow, or red. Aim for green for performant applications. + +You can enable production profiling for React with the `--profile` flag in `next build`. This requires [Next.js 9.5](https://nextjs.org/blog/next-9-5): + +```bash +next build --profile +``` + +After that, you can use the profiler in the same way as you would in development. + +You can enable more verbose build output with the `--debug` flag in `next build`. This requires Next.js 9.5.3: + +```bash +next build --debug +``` + +With this flag enabled additional build output like rewrites, redirects, and headers will be shown. ## Development @@ -58,6 +74,20 @@ The application will start at `http://localhost:3000` by default. The default po npx next dev -p 4000 ``` +Or using the `PORT` environment variable: + +```bash +PORT=4000 npx next dev +``` + +> Note: `PORT` can not be set in `.env` as booting up the HTTP server happens before any other code is initialized. + +You can also set the hostname to be different from the default of `0.0.0.0`, this can be useful for making the application available for other devices on the network. The default hostname can be changed with `-H`, like so: + +```bash +npx next dev -H 192.168.1.2 +``` + ## Production `next start` starts the application in production mode. The application should be compiled with [`next build`](#build) first. @@ -68,6 +98,27 @@ The application will start at `http://localhost:3000` by default. The default po npx next start -p 4000 ``` +Or using the `PORT` environment variable: + +```bash +PORT=4000 npx next start +``` + +> Note: `PORT` can not be set in `.env` as booting up the HTTP server happens before any other code is initialized. + +## Lint + +`next lint` runs ESLint for all files in the `pages`, `components`, and `lib` directories. It also +provides a guided setup to install any required dependencies if ESLint is not already configured in +your application. + +If you have other directories that you would like to lint, you can specify them using the `--dir` +flag: + +```bash +next lint --dir utils +``` + ## Telemetry Next.js collects **completely anonymous** telemetry data about general usage. diff --git a/docs/api-reference/create-next-app.md b/docs/api-reference/create-next-app.md new file mode 100644 index 0000000000000..1192865d09c00 --- /dev/null +++ b/docs/api-reference/create-next-app.md @@ -0,0 +1,65 @@ +--- +description: Create Next.js apps in one command with create-next-app. +--- + +# Create Next App + +The easiest way to get started with Next.js is by using `create-next-app`. This CLI tool enables you to quickly start building a new Next.js application, with everything set up for you. You can create a new app using the default Next.js template, or by using one of the [official Next.js examples](https://github.com/vercel/next.js/tree/canary/examples). To get started, use the following command: + +```bash +npx create-next-app +# or +yarn create next-app +``` + +You can create a [TypeScript project](https://github.com/vercel/next.js/blob/canary/docs/basic-features/typescript.md) with the `--ts, --typescript` flag: + +```bash +npx create-next-app --ts +# or +yarn create next-app --typescript +``` + +### Options + +`create-next-app` comes with the following options: + +- **--ts, --typescript** - Initialize as a TypeScript project. +- **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/master/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. +- **--example-path [path-to-example]** - In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: `--example-path foo/bar` +- **--use-npm** - Explicitly tell the CLI to bootstrap the app using npm. To bootstrap using yarn we recommend running `yarn create next-app` + +### Why use Create Next App? + +`create-next-app` allows you to create a new Next.js app within seconds. It is officially maintained by the creators of Next.js, and includes a number of benefits: + +- **Interactive Experience**: Running `npx create-next-app` (with no arguments) launches an interactive experience that guides you through setting up a project. +- **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. +- **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. +- **Support for Examples**: Create Next App can bootstrap your application using an example from the Next.js examples collection (e.g. `npx create-next-app --example api-routes`). +- **Tested**: The package is part of the Next.js monorepo and tested using the same integration test suite as Next.js itself, ensuring it works as expected with every release. + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/basic-features/pages.md"> + <b>Pages:</b> + <small>Learn more about what pages are in Next.js.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/built-in-css-support.md"> + <b>CSS Support:</b> + <small>Use the built-in CSS support to add custom styles to your app.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/api-reference/cli.md"> + <b>CLI:</b> + <small>Learn more about the Next.js CLI.</small> + </a> +</div> diff --git a/docs/api-reference/data-fetching/getInitialProps.md b/docs/api-reference/data-fetching/getInitialProps.md index d4590c9e797d8..f46b34615f563 100644 --- a/docs/api-reference/data-fetching/getInitialProps.md +++ b/docs/api-reference/data-fetching/getInitialProps.md @@ -4,19 +4,11 @@ description: Enable Server-Side Rendering in a page and do initial data populati # getInitialProps -> **Recommended: [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering)** +> **Recommended: [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering)**. > > If you're using Next.js 9.3 or newer, we recommend that you use `getStaticProps` or `getServerSideProps` instead of `getInitialProps`. > -> These new data fetching methods allow you to have a granular choice between static generation and server-side rendering. -> Learn more on the documentation for [Pages](/docs/basic-features/pages.md) and [Data fetching](/docs/basic-features/data-fetching.md): - -<details> - <summary><b>Examples</b></summary> - <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/data-fetch">Data fetch</a></li> - </ul> -</details> +> These new data fetching methods allow you to have a granular choice between static generation and server-side rendering. Learn more on the documentation for [Pages](/docs/basic-features/pages.md) and [Data Fetching](/docs/basic-features/data-fetching.md). `getInitialProps` enables [server-side rendering](/docs/basic-features/pages.md#server-side-rendering) in a page and allows you to do **initial data population**, it means sending the [page](/docs/basic-features/pages.md) with the data already populated from the server. This is especially useful for [SEO](https://en.wikipedia.org/wiki/Search_engine_optimization). @@ -62,7 +54,7 @@ export default Page Data returned from `getInitialProps` is serialized when server rendering, similar to what [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) does. Make sure the returned object from `getInitialProps` is a plain `Object` and not using `Date`, `Map` or `Set`. -For the initial page load, `getInitialProps` will run on the server only. `getInitialProps` will then run on the client when navigating to a different route via the [`next/link`](/docs/api-reference/next/link.md) component or by using [`next/router`](/docs/api-reference/next/router.md). +For the initial page load, `getInitialProps` will run on the server only. `getInitialProps` will then run on the client when navigating to a different route via the [`next/link`](/docs/api-reference/next/link.md) component or by using [`next/router`](/docs/api-reference/next/router.md). However, if `getInitialProps` is used in a custom `_app.js`, and the page being navigated to implements `getServerSideProps`, then `getInitialProps` will run on the server. ## Context Object @@ -71,8 +63,8 @@ For the initial page load, `getInitialProps` will run on the server only. `getIn - `pathname` - Current route. That is the path of the page in `/pages` - `query` - Query string section of URL parsed as an object - `asPath` - `String` of the actual path (including the query) shown in the browser -- `req` - HTTP request object (server only) -- `res` - HTTP response object (server only) +- `req` - [HTTP request object](https://nodejs.org/api/http.html#http_class_http_incomingmessage 'Class: http.IncomingMessage HTTP | Node.js v14.8.0 Documentation') (server only) +- `res` - [HTTP response object](https://nodejs.org/api/http.html#http_class_http_serverresponse 'Class: http.ServerResponse HTTP | Node.js v14.8.0 Documentation') (server only) - `err` - Error object if any error is encountered during the rendering ## Caveats diff --git a/docs/api-reference/next.config.js/basepath.md b/docs/api-reference/next.config.js/basepath.md new file mode 100644 index 0000000000000..0df919b35a057 --- /dev/null +++ b/docs/api-reference/next.config.js/basepath.md @@ -0,0 +1,79 @@ +--- +description: Learn more about setting a base path in Next.js +--- + +# Base Path + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| -------- | ---------------- | +| `v9.5.0` | Base Path added. | + +</details> + +To deploy a Next.js application under a sub-path of a domain you can use the `basePath` config option. + +`basePath` allows you to set a path prefix for the application. For example, to use `/docs` instead of `/` (the default), open `next.config.js` and add the `basePath` config: + +```js +module.exports = { + basePath: '/docs', +} +``` + +Note: this value must be set at build time and can not be changed without re-building as the value is inlined in the client-side bundles. + +## Links + +When linking to other pages using `next/link` and `next/router` the `basePath` will be automatically applied. + +For example, using `/about` will automatically become `/docs/about` when `basePath` is set to `/docs`. + +```js +export default function HomePage() { + return ( + <> + <Link href="/about"> + <a>About Page</a> + </Link> + </> + ) +} +``` + +Output html: + +```html +<a href="/docs/about">About Page</a> +``` + +This makes sure that you don't have to change all links in your application when changing the `basePath` value. + +## Images + +When using the [`next/image`](/docs/api-reference/next/image.md) component, you will need to add the `basePath` in front of `src`. + +For example, using `/docs/me.png` will properly serve your image when `basePath` is set to `/docs`. + +```jsx +import Image from 'next/image' + +function Home() { + return ( + <> + <h1>My Homepage</h1> + <Image + src="/docs/me.png" + alt="Picture of the author" + width={500} + height={500} + /> + <p>Welcome to my homepage!</p> + </> + ) +} + +export default Home +``` diff --git a/docs/api-reference/next.config.js/build-target.md b/docs/api-reference/next.config.js/build-target.md deleted file mode 100644 index 3de94984c77b0..0000000000000 --- a/docs/api-reference/next.config.js/build-target.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -description: Learn more about the build targets used by Next.js, which decide the way your application is built and run. ---- - -# Build Target - -Next.js supports various build targets, each changing the way your application is built and run. We'll explain each of the targets below. - -## `server` target - -> This is the default target, however, we highly recommend the [`serverless` target](#serverless-target). The `serverless` target enforces [additional constraints](https://rauchg.com/2020/2019-in-review#serverless-upgrades-itself) to keep you in the [Pit of Success](https://blog.codinghorror.com/falling-into-the-pit-of-success/). - -This target is compatible with both `next start` and [custom server](/docs/advanced-features/custom-server.md) setups (it's mandatory for a custom server). - -Your application will be built and deployed as a monolith. This is the default target and no action is required on your part to opt-in. - -## `serverless` target - -> Deployments to [Vercel](https://vercel.com) will automatically enable this target. You should not opt-into it yourself. - -This target will output independent pages that don't require a monolithic server. - -It's only compatible with `next start` or Serverless deployment platforms (like [Vercel](https://vercel.com)) — you cannot use the custom server API. - -To opt-into this target, set the following configuration in your `next.config.js`: - -```js -module.exports = { - target: 'serverless', -} -``` - -## Related - -<div class="card"> - <a href="/docs/api-reference/next.config.js/introduction.md"> - <b>Introduction to next.config.js:</b> - <small>Learn more about the configuration file used by Next.js.</small> - </a> -</div> - -<div class="card"> - <a href="/docs/deployment.md"> - <b>Deployment:</b> - <small>Compile and deploy your Next.js app to production.</small> - </a> -</div> diff --git a/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md b/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md index 0f04a3de29d39..20056521d6e78 100644 --- a/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md +++ b/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md @@ -4,6 +4,13 @@ description: A custom asset prefix allows you serve static assets from a CDN. Le # CDN Support with Asset Prefix +> **Attention**: [Deploying to Vercel](/docs/deployment.md) automatically configures a global CDN for your Next.js project. +> You do not need to manually setup an Asset Prefix. + +> **Note**: Next.js 9.5+ added support for a customizable [Base Path](/docs/api-reference/next.config.js/basepath.md), which is better +> suited for hosting your application on a sub-path like `/docs`. +> We do not suggest you use a custom Asset Prefix for this use case. + To set up a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network), you can set up an asset prefix and configure your CDN's origin to resolve to the domain that Next.js is hosted on. Open `next.config.js` and add the `assetPrefix` config: @@ -17,7 +24,25 @@ module.exports = { } ``` -Next.js will automatically use your prefix in the scripts it loads, but this has no effect whatsoever on the [public](/docs/basic-features/static-file-serving.md) folder; if you want to serve those assets over a CDN, you'll have to introduce the prefix yourself. One way of introducing a prefix that works inside your components and varies by environment is documented [in this example](https://github.com/vercel/next.js/tree/canary/examples/with-universal-configuration-build-time). +Next.js will automatically use your asset prefix for the JavaScript and CSS files it loads from the `/_next/` path (`.next/static/` folder). For example, with the above configuration, the following request for a JS chunk: + +``` +/_next/static/chunks/4b9b41aaa062cbbfeff4add70f256968c51ece5d.4d708494b3aed70c04f0.js +``` + +Would instead become: + +``` +https://cdn.mydomain.com/_next/static/chunks/4b9b41aaa062cbbfeff4add70f256968c51ece5d.4d708494b3aed70c04f0.js +``` + +The exact configuration for uploading your files to a given CDN will depend on your CDN of choice. The only folder you need to host on your CDN is the contents of `.next/static/`, which should be uploaded as `_next/static/` as the above URL request indicates. **Do not upload the rest of your `.next/` folder**, as you should not expose your server code and other configuration to the public. + +While `assetPrefix` covers requests to `_next/static`, it does not influence the following paths: + +- Files in the [public](/docs/basic-features/static-file-serving.md) folder; if you want to serve those assets over a CDN, you'll have to introduce the prefix yourself +- `/_next/data/` requests for `getServerSideProps` pages. These requests will always be made against the main domain since they're not static. +- `/_next/data/` requests for `getStaticProps` pages. These requests will always be made against the main domain to support [Incremental Static Generation](/docs/basic-features/data-fetching.md#incremental-static-regeneration), even if you're not using it (for consistency). ## Related diff --git a/docs/api-reference/next.config.js/compression.md b/docs/api-reference/next.config.js/compression.md index c20ebe798e1c7..6947040d2ec01 100644 --- a/docs/api-reference/next.config.js/compression.md +++ b/docs/api-reference/next.config.js/compression.md @@ -4,7 +4,7 @@ description: Next.js provides gzip compression to compress rendered content and # Compression -Next.js provides [**gzip**](https://tools.ietf.org/html/rfc6713#section-3) compression to compress rendered content and static files. Compression only works with the [`server` target](/docs/api-reference/next.config.js/build-target.md#server-target). In general you will want to enable compression on a HTTP proxy like [nginx](https://www.nginx.com/), to offload load from the `Node.js` process. +Next.js provides [**gzip**](https://tools.ietf.org/html/rfc6713#section-3) compression to compress rendered content and static files. In general you will want to enable compression on a HTTP proxy like [nginx](https://www.nginx.com/), to offload load from the `Node.js` process. To disable **compression**, open `next.config.js` and disable the `compress` config: diff --git a/docs/api-reference/next.config.js/custom-page-extensions.md b/docs/api-reference/next.config.js/custom-page-extensions.md index 436af982cc58f..dc11fdc6fac1c 100644 --- a/docs/api-reference/next.config.js/custom-page-extensions.md +++ b/docs/api-reference/next.config.js/custom-page-extensions.md @@ -10,10 +10,41 @@ Open `next.config.js` and add the `pageExtensions` config: ```js module.exports = { - pageExtensions: ['mdx', 'jsx', 'js', 'ts', 'tsx'], + pageExtensions: ['mdx', 'md', 'jsx', 'js', 'tsx', 'ts'], } ``` +> **Note**: The default value of `pageExtensions` is [`['tsx', 'ts', 'jsx', 'js']`](https://github.com/vercel/next.js/blob/f1dbc9260d48c7995f6c52f8fbcc65f08e627992/packages/next/server/config-shared.ts#L161). + +> **Note**: configuring `pageExtensions` also affects `_document.js`, `_app.js` as well as files under `pages/api/`. For example, setting `pageExtensions: ['page.tsx', 'page.ts']` means the following files: `_document.tsx`, `_app.tsx`, `pages/users.tsx` and `pages/api/users.ts` will have to be renamed to `_document.page.tsx`, `_app.page.tsx`, `pages/users.page.tsx` and `pages/api/users.page.ts` respectively. + +## Including non-page files in the `pages` directory + +To colocate test files, generated files, or other files used by components in the `pages` directory, you can prefix the extensions with something like `page`. + +Open `next.config.js` and add the `pageExtensions` config: + +```js +module.exports = { + pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'], +} +``` + +Then rename your pages to have a file extension that includes `.page` (ex. rename `MyPage.tsx` to `MyPage.page.tsx`). + +> **Note**: Make sure you also rename `_document.js`, `_app.js` as well as files under `pages/api/`. + +Without this config, Next.js assumes every tsx/ts/jsx/js file in the `pages` directory is a page or API route, and may expose unintended routes vulnerable to denial of service attacks, or throw an error like the following when building the production bundle: + +``` +Build error occurred +Error: Build optimization failed: found pages without a React Component as default export in +pages/MyPage.generated +pages/MyPage.test + +See https://nextjs.org/docs/messages/page-without-valid-component for more info. +``` + ## Related <div class="card"> diff --git a/docs/api-reference/next.config.js/custom-webpack-config.md b/docs/api-reference/next.config.js/custom-webpack-config.md index 618bafe42790a..6b4535c7e80f5 100644 --- a/docs/api-reference/next.config.js/custom-webpack-config.md +++ b/docs/api-reference/next.config.js/custom-webpack-config.md @@ -4,12 +4,17 @@ description: Extend the default webpack config added by Next.js. # Custom Webpack Config +Before continuing to add custom webpack configuration to your application make sure Next.js doesn't already support your use-case: + +- [CSS imports](/docs/basic-features/built-in-css-support.md#adding-a-global-stylesheet) +- [CSS modules](/docs/basic-features/built-in-css-support.md#adding-component-level-css) +- [Sass/SCSS imports](/docs/basic-features/built-in-css-support.md#sass-support) +- [Sass/SCSS modules](/docs/basic-features/built-in-css-support.md#sass-support) +- [preact](https://github.com/vercel/next.js/tree/canary/examples/using-preact) +- [Customizing babel configuration](/docs/advanced-features/customizing-babel-config.md) + Some commonly asked for features are available as plugins: -- [@zeit/next-sass](https://github.com/zeit/next-plugins/tree/master/packages/next-sass) -- [@zeit/next-less](https://github.com/zeit/next-plugins/tree/master/packages/next-less) -- [@zeit/next-stylus](https://github.com/zeit/next-plugins/tree/master/packages/next-stylus) -- [@zeit/next-preact](https://github.com/zeit/next-plugins/tree/master/packages/next-preact) - [@next/mdx](https://github.com/vercel/next.js/tree/canary/packages/next-mdx) - [@next/bundle-analyzer](https://github.com/vercel/next.js/tree/canary/packages/next-bundle-analyzer) @@ -18,14 +23,6 @@ In order to extend our usage of `webpack`, you can define a function that extend ```js module.exports = { webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => { - // Note: we provide webpack above so you should not `require` it - // Perform customizations to webpack config - // Important: return the modified config - config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//)) - return config - }, - webpackDevMiddleware: (config) => { - // Perform customizations to webpack dev middleware config // Important: return the modified config return config }, diff --git a/docs/api-reference/next.config.js/disabling-http-keep-alive.md b/docs/api-reference/next.config.js/disabling-http-keep-alive.md new file mode 100644 index 0000000000000..bfa79ad3d8b6d --- /dev/null +++ b/docs/api-reference/next.config.js/disabling-http-keep-alive.md @@ -0,0 +1,36 @@ +--- +description: Next.js will automatically use HTTP Keep-Alive by default. Learn more about how to disable HTTP Keep-Alive here. +--- + +# Disabling HTTP Keep-Alive + +Next.js automatically polyfills [node-fetch](/docs/basic-features/supported-browsers-features#polyfills) and enables [HTTP Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive) by default. You may want to disable HTTP Keep-Alive for certain `fetch()` calls or globally. + +For a single `fetch()` call, you can add the agent option: + +```js +import { Agent } from 'https' + +const url = 'https://example.com' +const agent = new Agent({ keepAlive: false }) +fetch(url, { agent }) +``` + +To override all `fetch()` calls globally, you can use `next.config.js`: + +```js +module.exports = { + httpAgentOptions: { + keepAlive: false, + }, +} +``` + +## Related + +<div class="card"> + <a href="/docs/api-reference/next.config.js/introduction.md"> + <b>Introduction to next.config.js:</b> + <small>Learn more about the configuration file used by Next.js.</small> + </a> +</div> diff --git a/docs/api-reference/next.config.js/environment-variables.md b/docs/api-reference/next.config.js/environment-variables.md index 9a3536fa0c9b3..582f1449b60df 100644 --- a/docs/api-reference/next.config.js/environment-variables.md +++ b/docs/api-reference/next.config.js/environment-variables.md @@ -4,13 +4,12 @@ description: Learn to add and access environment variables in your Next.js appli # Environment Variables -> Since the release of Next.js 9.4 we now have a more intuitive and ergonomic experience for [adding environment variables](/docs/basic-features/environment-variables.md). Give it a try! +> Since the release of [Next.js 9.4](https://nextjs.org/blog/next-9-4) we now have a more intuitive and ergonomic experience for [adding environment variables](/docs/basic-features/environment-variables.md). Give it a try! <details> <summary><b>Examples</b></summary> <ul> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-env-from-next-config-js">With env</a></li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-now-env">With Now env</a></li> </ul> </details> diff --git a/docs/api-reference/next.config.js/exportPathMap.md b/docs/api-reference/next.config.js/exportPathMap.md index 1e87499a33065..a5e4420ffa112 100644 --- a/docs/api-reference/next.config.js/exportPathMap.md +++ b/docs/api-reference/next.config.js/exportPathMap.md @@ -4,7 +4,7 @@ description: Customize the pages that will be exported as HTML files when using # exportPathMap -> This feature is exclusive of `next export`. Please refer to [Static HTML export](/docs/advanced-features/static-html-export.md) if you want to learn more about it. +> This feature is exclusive to `next export`. Please refer to [Static HTML export](/docs/advanced-features/static-html-export.md) if you want to learn more about it. <details> <summary><b>Examples</b></summary> @@ -13,6 +13,8 @@ description: Customize the pages that will be exported as HTML files when using </ul> </details> +`exportPathMap` allows you to specify a mapping of request paths to page destinations, to be used during export. Paths defined in `exportPathMap` will also be available when using [`next dev`](/docs/api-reference/cli.md#development). + Let's start with an example, to create a custom `exportPathMap` for an app with the following pages: - `pages/index.js` @@ -38,13 +40,15 @@ module.exports = { } ``` +Note: the `query` field in `exportPathMap` cannot be used with [automatically statically optimized pages](/docs/advanced-features/automatic-static-optimization) or [`getStaticProps` pages](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) as they are rendered to HTML files at build-time and additional query information cannot be provided during `next export`. + The pages will then be exported as HTML files, for example, `/about` will become `/about.html`. `exportPathMap` is an `async` function that receives 2 arguments: the first one is `defaultPathMap`, which is the default map used by Next.js. The second argument is an object with: - `dev` - `true` when `exportPathMap` is being called in development. `false` when running `next export`. In development `exportPathMap` is used to define routes. - `dir` - Absolute path to the project directory -- `outDir` - Absolute path to the `out/` directory (configurable with `-o`). When `dev` is `true` the value of `outDir` will be `null`. +- `outDir` - Absolute path to the `out/` directory ([configurable with `-o`](#customizing-the-output-directory)). When `dev` is `true` the value of `outDir` will be `null`. - `distDir` - Absolute path to the `.next/` directory (configurable with the [`distDir`](/docs/api-reference/next.config.js/setting-a-custom-build-directory.md) config) - `buildId` - The generated build id @@ -59,14 +63,22 @@ The returned object is a map of pages where the `key` is the `pathname` and the It is possible to configure Next.js to export pages as `index.html` files and require trailing slashes, `/about` becomes `/about/index.html` and is routable via `/about/`. This was the default behavior prior to Next.js 9. -To switch back and add a trailing slash, open `next.config.js` and enable the `exportTrailingSlash` config: +To switch back and add a trailing slash, open `next.config.js` and enable the `trailingSlash` config: ```js module.exports = { - exportTrailingSlash: true, + trailingSlash: true, } ``` +## Customizing the output directory + +[`next export`](/docs/advanced-features/static-html-export.md#how-to-use-it) will use `out` as the default output directory, you can customize this using the `-o` argument, like so: + +```bash +next export -o outdir +``` + ## Related <div class="card"> diff --git a/docs/api-reference/next.config.js/headers.md b/docs/api-reference/next.config.js/headers.md new file mode 100644 index 0000000000000..d64c01c676b79 --- /dev/null +++ b/docs/api-reference/next.config.js/headers.md @@ -0,0 +1,390 @@ +--- +description: Add custom HTTP headers to your Next.js app. +--- + +# Headers + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/headers">Headers</a></li> + </ul> +</details> + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | -------------- | +| `v10.2.0` | `has` added. | +| `v9.5.0` | Headers added. | + +</details> + +Headers allow you to set custom HTTP headers for an incoming request path. + +To set custom HTTP headers you can use the `headers` key in `next.config.js`: + +```js +module.exports = { + async headers() { + return [ + { + source: '/about', + headers: [ + { + key: 'x-custom-header', + value: 'my custom header value', + }, + { + key: 'x-another-custom-header', + value: 'my other custom header value', + }, + ], + }, + ] + }, +} +``` + +`headers` is an async function that expects an array to be returned holding objects with `source` and `headers` properties: + +- `source` is the incoming request path pattern. +- `headers` is an array of header objects with the `key` and `value` properties. +- `basePath`: `false` or `undefined` - if false the basePath won't be included when matching, can be used for external rewrites only. +- `locale`: `false` or `undefined` - whether the locale should not be included when matching. +- `has` is an array of [has objects](#header-cookie-and-query-matching) with the `type`, `key` and `value` properties. + +Headers are checked before the filesystem which includes pages and `/public` files. + +## Header Overriding Behavior + +If two headers match the same path and set the same header key, the last header key will override the first. Using the below headers, the path `/hello` will result in the header `x-hello` being `world` due to the last header value set being `world`. + +```js +module.exports = { + async headers() { + return [ + { + source: '/:path*', + headers: [ + { + key: 'x-hello', + value: 'there', + }, + ], + }, + { + source: '/hello', + headers: [ + { + key: 'x-hello', + value: 'world', + }, + ], + }, + ], + }, +} +``` + +## Path Matching + +Path matches are allowed, for example `/blog/:slug` will match `/blog/hello-world` (no nested paths): + +```js +module.exports = { + async headers() { + return [ + { + source: '/blog/:slug', + headers: [ + { + key: 'x-slug', + value: ':slug', // Matched parameters can be used in the value + }, + { + key: 'x-slug-:slug', // Matched parameters can be used in the key + value: 'my other custom header value', + }, + ], + }, + ], + }, +} +``` + +### Wildcard Path Matching + +To match a wildcard path you can use `*` after a parameter, for example `/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: + +```js +module.exports = { + async headers() { + return [ + { + source: '/blog/:slug*', + headers: [ + { + key: 'x-slug', + value: ':slug*', // Matched parameters can be used in the value + }, + { + key: 'x-slug-:slug*', // Matched parameters can be used in the key + value: 'my other custom header value', + }, + ], + }, + ], + }, +} +``` + +### Regex Path Matching + +To match a regex path you can wrap the regex in parenthesis after a parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but not `/blog/abc`: + +```js +module.exports = { + async headers() { + return [ + { + source: '/blog/:post(\\d{1,})', + headers: [ + { + key: 'x-post', + value: ':post', + }, + ], + }, + ], + }, +} +``` + +The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used for regex path matching, so when used in the `source` as non-special values they must be escaped by adding `\\` before them: + +```js +module.exports = { + async headers() { + return [ + { + // this will match `/english(default)/something` being requested + source: '/english\\(default\\)/:slug', + headers: [ + { + key: 'x-header', + value: 'value', + }, + ], + }, + ] + }, +} +``` + +## Header, Cookie, and Query Matching + +To only apply a header when either header, cookie, or query values also match the `has` field can be used. Both the `source` and all `has` items must match for the header to be applied. + +`has` items have the following fields: + +- `type`: `String` - must be either `header`, `cookie`, `host`, or `query`. +- `key`: `String` - the key from the selected type to match against. +- `value`: `String` or `undefined` - the value to check for, if undefined any value will match. A regex like string can be used to capture a specific part of the value, e.g. if the value `first-(?<paramName>.*)` is used for `first-second` then `second` will be usable in the destination with `:paramName`. + +```js +module.exports = { + async headers() { + return [ + // if the header `x-add-header` is present, + // the `x-another-header` header will be applied + { + source: '/:path*', + has: [ + { + type: 'header', + key: 'x-add-header', + }, + ], + headers: [ + { + key: 'x-another-header', + value: 'hello', + }, + ], + }, + // if the source, query, and cookie are matched, + // the `x-authorized` header will be applied + { + source: '/specific/:path*', + has: [ + { + type: 'query', + key: 'page', + // the page value will not be available in the + // header key/values since value is provided and + // doesn't use a named capture group e.g. (?<page>home) + value: 'home', + }, + { + type: 'cookie', + key: 'authorized', + value: 'true', + }, + ], + headers: [ + { + key: 'x-authorized', + value: ':authorized', + }, + ], + }, + // if the header `x-authorized` is present and + // contains a matching value, the `x-another-header` will be applied + { + source: '/:path*', + has: [ + { + type: 'header', + key: 'x-authorized', + value: '(?<authorized>yes|true)', + }, + ], + headers: [ + { + key: 'x-another-header', + value: ':authorized', + }, + ], + }, + // if the host is `example.com`, + // this header will be applied + { + source: '/:path*', + has: [ + { + type: 'host', + value: 'example.com', + }, + ], + headers: [ + { + key: 'x-another-header', + value: ':authorized', + }, + ], + }, + ] + }, +} +``` + +### Headers with basePath support + +When leveraging [`basePath` support](/docs/api-reference/next.config.js/basepath.md) with headers each `source` is automatically prefixed with the `basePath` unless you add `basePath: false` to the header: + +```js +module.exports = { + basePath: '/docs', + + async headers() { + return [ + { + source: '/with-basePath', // becomes /docs/with-basePath + headers: [ + { + key: 'x-hello', + value: 'world', + }, + ], + }, + { + source: '/without-basePath', // is not modified since basePath: false is set + headers: [ + { + key: 'x-hello', + value: 'world', + }, + ], + basePath: false, + }, + ] + }, +} +``` + +### Headers with i18n support + +When leveraging [`i18n` support](/docs/advanced-features/i18n-routing.md) with headers each `source` is automatically prefixed to handle the configured `locales` unless you add `locale: false` to the header. If `locale: false` is used you must prefix the `source` with a locale for it to be matched correctly. + +```js +module.exports = { + i18n: { + locales: ['en', 'fr', 'de'], + defaultLocale: 'en', + }, + + async headers() { + return [ + { + source: '/with-locale', // automatically handles all locales + headers: [ + { + key: 'x-hello', + value: 'world', + }, + ], + }, + { + // does not handle locales automatically since locale: false is set + source: '/nl/with-locale-manual', + locale: false, + headers: [ + { + key: 'x-hello', + value: 'world', + }, + ], + }, + { + // this matches '/' since `en` is the defaultLocale + source: '/en', + locale: false, + headers: [ + { + key: 'x-hello', + value: 'world', + }, + ], + }, + { + // this gets converted to /(en|fr|de)/(.*) so will not match the top-level + // `/` or `/fr` routes like /:path* would + source: '/(.*)', + headers: [ + { + key: 'x-hello', + value: 'worlld', + }, + ], + }, + ] + }, +} +``` + +### Cache-Control + +Cache-Control headers set in next.config.js will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](https://nextjs.org/docs/basic-features/pages#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) function. + +## Related + +For more information, we recommend the following sections: + +<div class="card"> + <a href="/docs/advanced-features/security-headers.md"> + <b>Security Headers:</b> + <small>Improve the security of your Next.js application by add HTTP response headers.</small> + </a> +</div> diff --git a/docs/api-reference/next.config.js/ignoring-eslint.md b/docs/api-reference/next.config.js/ignoring-eslint.md new file mode 100644 index 0000000000000..650c678bfd072 --- /dev/null +++ b/docs/api-reference/next.config.js/ignoring-eslint.md @@ -0,0 +1,37 @@ +--- +description: Next.js reports ESLint errors and warnings during builds by default. Learn how to opt-out of this behavior here. +--- + +# Ignoring ESLint + +When ESLint is detected in your project, Next.js fails your **production build** (`next build`) when errors are present. + +If you'd like Next.js to produce production code even when your application has ESLint errors, you can disable the built-in linting step completely. This is not recommended unless you already have ESLint configured to run in a separate part of your workflow (for example, in CI or a pre-commit hook). + +Open `next.config.js` and enable the `ignoreDuringBuilds` option in the `eslint` config: + +```js +module.exports = { + eslint: { + // Warning: This allows production builds to successfully complete even if + // your project has ESLint errors. + ignoreDuringBuilds: true, + }, +} +``` + +## Related + +<div class="card"> + <a href="/docs/api-reference/next.config.js/introduction.md"> + <b>Introduction to next.config.js:</b> + <small>Learn more about the configuration file used by Next.js.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/eslint.md"> + <b>ESLint:</b> + <small>Get started with ESLint in Next.js.</small> + </a> +</div> diff --git a/docs/api-reference/next.config.js/introduction.md b/docs/api-reference/next.config.js/introduction.md index effff047f1315..02bf1c5904bbf 100644 --- a/docs/api-reference/next.config.js/introduction.md +++ b/docs/api-reference/next.config.js/introduction.md @@ -11,22 +11,31 @@ For custom advanced behavior of Next.js, you can create a `next.config.js` in th Take a look at the following `next.config.js` example: ```js -module.exports = { +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { /* config options here */ } + +module.exports = nextConfig ``` You can also use a function: ```js module.exports = (phase, { defaultConfig }) => { - return { + /** + * @type {import('next').NextConfig} + */ + const nextConfig = { /* config options here */ } + return nextConfig } ``` -`phase` is the current context in which the configuration is loaded. You can see the available phases [here](https://github.com/vercel/next.js/blob/canary/packages/next/next-server/lib/constants.ts#L1-L4). Phases can be imported from `next/constants`: +`phase` is the current context in which the configuration is loaded. You can see the [available phases](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/constants.ts#L1-L4). Phases can be imported from `next/constants`: ```js const { PHASE_DEVELOPMENT_SERVER } = require('next/constants') @@ -44,8 +53,8 @@ module.exports = (phase, { defaultConfig }) => { } ``` -The commented lines are the place where you can put the configs allowed by `next.config.js`, which are defined [here](https://github.com/vercel/next.js/blob/canary/packages/next/next-server/server/config.ts#L12-L63). +The commented lines are the place where you can put the configs allowed by `next.config.js`, which are [defined in this file](https://github.com/vercel/next.js/blob/canary/packages/next/server/config-shared.ts#L68). -However, none of the configs are required, and it's not necessary to understand what each config does, instead, search for the features you need to enable or modify in this section and they will show you what to do. +However, none of the configs are required, and it's not necessary to understand what each config does. Instead, search for the features you need to enable or modify in this section and they will show you what to do. > Avoid using new JavaScript features not available in your target Node.js version. `next.config.js` will not be parsed by Webpack, Babel or TypeScript. diff --git a/docs/api-reference/next.config.js/react-strict-mode.md b/docs/api-reference/next.config.js/react-strict-mode.md new file mode 100644 index 0000000000000..b844bf84506fd --- /dev/null +++ b/docs/api-reference/next.config.js/react-strict-mode.md @@ -0,0 +1,29 @@ +--- +description: The complete Next.js runtime is now Strict Mode-compliant, learn how to opt-in +--- + +# React Strict Mode + +> **Suggested**: We strongly suggest you enable Strict Mode in your Next.js application to better prepare your application for the future of React. + +React's [Strict Mode](https://reactjs.org/docs/strict-mode.html) is a development mode only feature for highlighting potential problems in an application. It helps to identify unsafe lifecycles, legacy API usage, and a number of other features. + +The Next.js runtime is Strict Mode-compliant. To opt-in to Strict Mode, configure the following option in your `next.config.js`: + +```js +// next.config.js +module.exports = { + reactStrictMode: true, +} +``` + +If you or your team are not ready to use Strict Mode in your entire application, that's OK! You can incrementally migrate on a page-by-page basis using `<React.StrictMode>`. + +## Related + +<div class="card"> + <a href="/docs/api-reference/next.config.js/introduction.md"> + <b>Introduction to next.config.js:</b> + <small>Learn more about the configuration file used by Next.js.</small> + </a> +</div> diff --git a/docs/api-reference/next.config.js/redirects.md b/docs/api-reference/next.config.js/redirects.md new file mode 100644 index 0000000000000..9d35b97c34506 --- /dev/null +++ b/docs/api-reference/next.config.js/redirects.md @@ -0,0 +1,294 @@ +--- +description: Add redirects to your Next.js app. +--- + +# Redirects + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/redirects">Redirects</a></li> + </ul> +</details> + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ---------------- | +| `v10.2.0` | `has` added. | +| `v9.5.0` | Redirects added. | + +</details> + +Redirects allow you to redirect an incoming request path to a different destination path. + +Redirects are only available on the Node.js environment and do not affect client-side routing. + +To use Redirects you can use the `redirects` key in `next.config.js`: + +```js +module.exports = { + async redirects() { + return [ + { + source: '/about', + destination: '/', + permanent: true, + }, + ] + }, +} +``` + +`redirects` is an async function that expects an array to be returned holding objects with `source`, `destination`, and `permanent` properties: + +- `source` is the incoming request path pattern. +- `destination` is the path you want to route to. +- `permanent` if the redirect is permanent or not. +- `basePath`: `false` or `undefined` - if false the basePath won't be included when matching, can be used for external rewrites only. +- `locale`: `false` or `undefined` - whether the locale should not be included when matching. +- `has` is an array of [has objects](#header-cookie-and-query-matching) with the `type`, `key` and `value` properties. + +Redirects are checked before the filesystem which includes pages and `/public` files. + +When a redirect is applied, any query values provided in the request will be passed through to the redirect destination. For example, see the following redirect configuration: + +```js +{ + source: '/old-blog/:path*', + destination: '/blog/:path*', + permanent: false +} +``` + +When `/old-blog/post-1?hello=world` is requested, the client will be redirected to `/blog/post-1?hello=world`. + +## Path Matching + +Path matches are allowed, for example `/old-blog/:slug` will match `/old-blog/hello-world` (no nested paths): + +```js +module.exports = { + async redirects() { + return [ + { + source: '/old-blog/:slug', + destination: '/news/:slug', // Matched parameters can be used in the destination + permanent: true, + }, + ] + }, +} +``` + +### Wildcard Path Matching + +To match a wildcard path you can use `*` after a parameter, for example `/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: + +```js +module.exports = { + async redirects() { + return [ + { + source: '/blog/:slug*', + destination: '/news/:slug*', // Matched parameters can be used in the destination + permanent: true, + }, + ] + }, +} +``` + +### Regex Path Matching + +To match a regex path you can wrap the regex in parentheses after a parameter, for example `/post/:slug(\\d{1,})` will match `/post/123` but not `/post/abc`: + +```js +module.exports = { + async redirects() { + return [ + { + source: '/post/:slug(\\d{1,})', + destination: '/news/:slug', // Matched parameters can be used in the destination + permanent: false, + }, + ] + }, +} +``` + +The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used for regex path matching, so when used in the `source` as non-special values they must be escaped by adding `\\` before them: + +```js +module.exports = { + async redirects() { + return [ + { + // this will match `/english(default)/something` being requested + source: '/english\\(default\\)/:slug', + destination: '/en-us/:slug', + permanent: false, + }, + ] + }, +} +``` + +## Header, Cookie, and Query Matching + +To only match a redirect when header, cookie, or query values also match the `has` field can be used. Both the `source` and all `has` items must match for the redirect to be applied. + +`has` items have the following fields: + +- `type`: `String` - must be either `header`, `cookie`, `host`, or `query`. +- `key`: `String` - the key from the selected type to match against. +- `value`: `String` or `undefined` - the value to check for, if undefined any value will match. A regex like string can be used to capture a specific part of the value, e.g. if the value `first-(?<paramName>.*)` is used for `first-second` then `second` will be usable in the destination with `:paramName`. + +```js +module.exports = { + async redirects() { + return [ + // if the header `x-redirect-me` is present, + // this redirect will be applied + { + source: '/:path((?!another-page$).*)', + has: [ + { + type: 'header', + key: 'x-redirect-me', + }, + ], + permanent: false, + destination: '/another-page', + }, + // if the source, query, and cookie are matched, + // this redirect will be applied + { + source: '/specific/:path*', + has: [ + { + type: 'query', + key: 'page', + // the page value will not be available in the + // destination since value is provided and doesn't + // use a named capture group e.g. (?<page>home) + value: 'home', + }, + { + type: 'cookie', + key: 'authorized', + value: 'true', + }, + ], + permanent: false, + destination: '/another/:path*', + }, + // if the header `x-authorized` is present and + // contains a matching value, this redirect will be applied + { + source: '/', + has: [ + { + type: 'header', + key: 'x-authorized', + value: '(?<authorized>yes|true)', + }, + ], + permanent: false, + destination: '/home?authorized=:authorized', + }, + // if the host is `example.com`, + // this redirect will be applied + { + source: '/:path((?!another-page$).*)',, + has: [ + { + type: 'host', + value: 'example.com', + }, + ], + destination: '/another-page', + }, + ] + }, +} +``` + +### Redirects with basePath support + +When leveraging [`basePath` support](/docs/api-reference/next.config.js/basepath.md) with redirects each `source` and `destination` is automatically prefixed with the `basePath` unless you add `basePath: false` to the redirect: + +```js +module.exports = { + basePath: '/docs', + + async redirects() { + return [ + { + source: '/with-basePath', // automatically becomes /docs/with-basePath + destination: '/another', // automatically becomes /docs/another + permanent: false, + }, + { + // does not add /docs since basePath: false is set + source: '/without-basePath', + destination: '/another', + basePath: false, + permanent: false, + }, + ] + }, +} +``` + +### Redirects with i18n support + +When leveraging [`i18n` support](/docs/advanced-features/i18n-routing.md) with redirects each `source` and `destination` is automatically prefixed to handle the configured `locales` unless you add `locale: false` to the redirect. If `locale: false` is used you must prefix the `source` and `destination` with a locale for it to be matched correctly. + +```js +module.exports = { + i18n: { + locales: ['en', 'fr', 'de'], + defaultLocale: 'en', + }, + + async redirects() { + return [ + { + source: '/with-locale', // automatically handles all locales + destination: '/another', // automatically passes the locale on + permanent: false, + }, + { + // does not handle locales automatically since locale: false is set + source: '/nl/with-locale-manual', + destination: '/nl/another', + locale: false, + permanent: false, + }, + { + // this matches '/' since `en` is the defaultLocale + source: '/en', + destination: '/en/another', + locale: false, + permanent: false, + }, + { + // this gets converted to /(en|fr|de)/(.*) so will not match the top-level + // `/` or `/fr` routes like /:path* would + source: '/(.*)', + destination: '/another', + permanent: false, + }, + ] + }, +} +``` + +In some rare cases, you might need to assign a custom status code for older HTTP Clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both. Note: to ensure IE11 compatibility a `Refresh` header is automatically added for the 308 status code. + +## Other Redirects + +- Inside [API Routes](/docs/api-routes/response-helpers.md), you can use `res.redirect()`. +- Inside [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) and [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering), you can redirect specific pages at request-time. diff --git a/docs/api-reference/next.config.js/rewrites.md b/docs/api-reference/next.config.js/rewrites.md new file mode 100644 index 0000000000000..46c58eb6035dd --- /dev/null +++ b/docs/api-reference/next.config.js/rewrites.md @@ -0,0 +1,406 @@ +--- +description: Add rewrites to your Next.js app. +--- + +# Rewrites + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/rewrites">Rewrites</a></li> + </ul> +</details> + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | --------------- | +| `v10.2.0` | `has` added. | +| `v9.5.0` | Rewrites added. | + +</details> + +Rewrites allow you to map an incoming request path to a different destination path. + +Rewrites act as a URL proxy and mask the destination path, making it appear the user hasn't changed their location on the site. In contrast, [redirects](/docs/api-reference/next.config.js/redirects.md) will reroute to a new page and show the URL changes. + +To use rewrites you can use the `rewrites` key in `next.config.js`: + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/about', + destination: '/', + }, + ] + }, +} +``` + +Rewrites are applied to client-side routing, a `<Link href="/about">` will have the rewrite applied in the above example. + +`rewrites` is an async function that expects an array to be returned holding objects with `source` and `destination` properties: + +- `source`: `String` - is the incoming request path pattern. +- `destination`: `String` is the path you want to route to. +- `basePath`: `false` or `undefined` - if false the basePath won't be included when matching, can be used for external rewrites only. +- `locale`: `false` or `undefined` - whether the locale should not be included when matching. +- `has` is an array of [has objects](#header-cookie-and-query-matching) with the `type`, `key` and `value` properties. + +Rewrites are applied after checking the filesystem (pages and `/public` files) and before dynamic routes by default. This behavior can be changed by instead returning an object instead of an array from the `rewrites` function since `v10.1` of Next.js: + +```js +module.exports = { + async rewrites() { + return { + beforeFiles: [ + // These rewrites are checked after headers/redirects + // and before all files including _next/public files which + // allows overriding page files + { + source: '/some-page', + destination: '/somewhere-else', + has: [{ type: 'query', key: 'overrideMe' }], + }, + ], + afterFiles: [ + // These rewrites are checked after pages/public files + // are checked but before dynamic routes + { + source: '/non-existent', + destination: '/somewhere-else', + }, + ], + fallback: [ + // These rewrites are checked after both pages/public files + // and dynamic routes are checked + { + source: '/:path*', + destination: `https://my-old-site.com/:path*`, + }, + ], + } + }, +} +``` + +Note: rewrites in `beforeFiles` do not check the filesystem/dynamic routes immediately after matching a source, they continue until all `beforeFiles` have been checked. + +The order Next.js routes are checked is: + +1. [headers](/docs/api-reference/next.config.js/headers) are checked/applied +2. [redirects](/docs/api-reference/next.config.js/redirects) are checked/applied +3. `beforeFiles` rewrites are checked/applied +4. static files from the [public directory](/docs/basic-features/static-file-serving), `_next/static` files, and non-dynamic pages are checked/served +5. `afterFiles` rewrites are checked/applied, if one of these rewrites is matched we check dynamic routes/static files after each match +6. `fallback` rewrites are checked/applied, these are applied before rendering the 404 page and after dynamic routes/all static assets have been checked. + +## Rewrite parameters + +When using parameters in a rewrite the parameters will be passed in the query by default when none of the parameters are used in the `destination`. + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/old-about/:path*', + destination: '/about', // The :path parameter isn't used here so will be automatically passed in the query + }, + ] + }, +} +``` + +If a parameter is used in the destination none of the parameters will be automatically passed in the query. + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/docs/:path*', + destination: '/:path*', // The :path parameter is used here so will not be automatically passed in the query + }, + ] + }, +} +``` + +You can still pass the parameters manually in the query if one is already used in the destination by specifying the query in the `destination`. + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/:first/:second', + destination: '/:first?second=:second', + // Since the :first parameter is used in the destination the :second parameter + // will not automatically be added in the query although we can manually add it + // as shown above + }, + ] + }, +} +``` + +## Path Matching + +Path matches are allowed, for example `/blog/:slug` will match `/blog/hello-world` (no nested paths): + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/blog/:slug', + destination: '/news/:slug', // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +### Wildcard Path Matching + +To match a wildcard path you can use `*` after a parameter, for example `/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/blog/:slug*', + destination: '/news/:slug*', // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +### Regex Path Matching + +To match a regex path you can wrap the regex in parenthesis after a parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but not `/blog/abc`: + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/old-blog/:post(\\d{1,})', + destination: '/blog/:post', // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used for regex path matching, so when used in the `source` as non-special values they must be escaped by adding `\\` before them: + +```js +module.exports = { + async rewrites() { + return [ + { + // this will match `/english(default)/something` being requested + source: '/english\\(default\\)/:slug', + destination: '/en-us/:slug', + }, + ] + }, +} +``` + +## Header, Cookie, and Query Matching + +To only match a rewrite when header, cookie, or query values also match the `has` field can be used. Both the `source` and all `has` items must match for the rewrite to be applied. + +`has` items have the following fields: + +- `type`: `String` - must be either `header`, `cookie`, `host`, or `query`. +- `key`: `String` - the key from the selected type to match against. +- `value`: `String` or `undefined` - the value to check for, if undefined any value will match. A regex like string can be used to capture a specific part of the value, e.g. if the value `first-(?<paramName>.*)` is used for `first-second` then `second` will be usable in the destination with `:paramName`. + +```js +module.exports = { + async rewrites() { + return [ + // if the header `x-rewrite-me` is present, + // this rewrite will be applied + { + source: '/:path*', + has: [ + { + type: 'header', + key: 'x-rewrite-me', + }, + ], + destination: '/another-page', + }, + // if the source, query, and cookie are matched, + // this rewrite will be applied + { + source: '/specific/:path*', + has: [ + { + type: 'query', + key: 'page', + // the page value will not be available in the + // destination since value is provided and doesn't + // use a named capture group e.g. (?<page>home) + value: 'home', + }, + { + type: 'cookie', + key: 'authorized', + value: 'true', + }, + ], + destination: '/:path*/home', + }, + // if the header `x-authorized` is present and + // contains a matching value, this rewrite will be applied + { + source: '/:path*', + has: [ + { + type: 'header', + key: 'x-authorized', + value: '(?<authorized>yes|true)', + }, + ], + destination: '/home?authorized=:authorized', + }, + // if the host is `example.com`, + // this rewrite will be applied + { + source: '/:path*', + has: [ + { + type: 'host', + value: 'example.com', + }, + ], + destination: '/another-page', + }, + ] + }, +} +``` + +## Rewriting to an external URL + +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/custom-routes-proxying">Incremental adoption of Next.js</a></li> + </ul> +</details> + +Rewrites allow you to rewrite to an external url. This is especially useful for incrementally adopting Next.js. + +```js +module.exports = { + async rewrites() { + return [ + { + source: '/blog/:slug', + destination: 'https://example.com/blog/:slug', // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +### Incremental adoption of Next.js + +You can also have Next.js fall back to proxying to an existing website after checking all Next.js routes. + +This way you don't have to change the rewrites configuration when migrating more pages to Next.js + +```js +module.exports = { + async rewrites() { + return { + fallback: [ + { + source: '/:path*', + destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`, + }, + ], + } + }, +} +``` + +See additional information on incremental adoption [in the docs here](/docs/migrating/incremental-adoption.md). + +### Rewrites with basePath support + +When leveraging [`basePath` support](/docs/api-reference/next.config.js/basepath.md) with rewrites each `source` and `destination` is automatically prefixed with the `basePath` unless you add `basePath: false` to the rewrite: + +```js +module.exports = { + basePath: '/docs', + + async rewrites() { + return [ + { + source: '/with-basePath', // automatically becomes /docs/with-basePath + destination: '/another', // automatically becomes /docs/another + }, + { + // does not add /docs to /without-basePath since basePath: false is set + // Note: this can not be used for internal rewrites e.g. `destination: '/another'` + source: '/without-basePath', + destination: 'https://example.com', + basePath: false, + }, + ] + }, +} +``` + +### Rewrites with i18n support + +When leveraging [`i18n` support](/docs/advanced-features/i18n-routing.md) with rewrites each `source` and `destination` is automatically prefixed to handle the configured `locales` unless you add `locale: false` to the rewrite. If `locale: false` is used you must prefix the `source` and `destination` with a locale for it to be matched correctly. + +```js +module.exports = { + i18n: { + locales: ['en', 'fr', 'de'], + defaultLocale: 'en', + }, + + async rewrites() { + return [ + { + source: '/with-locale', // automatically handles all locales + destination: '/another', // automatically passes the locale on + }, + { + // does not handle locales automatically since locale: false is set + source: '/nl/with-locale-manual', + destination: '/nl/another', + locale: false, + }, + { + // this matches '/' since `en` is the defaultLocale + source: '/en', + destination: '/en/another', + locale: false, + }, + { + // this gets converted to /(en|fr|de)/(.*) so will not match the top-level + // `/` or `/fr` routes like /:path* would + source: '/(.*)', + destination: '/another', + }, + ] + }, +} +``` diff --git a/docs/api-reference/next.config.js/runtime-configuration.md b/docs/api-reference/next.config.js/runtime-configuration.md index 4a4a2a5ace6e1..27ed9919542b2 100644 --- a/docs/api-reference/next.config.js/runtime-configuration.md +++ b/docs/api-reference/next.config.js/runtime-configuration.md @@ -4,9 +4,7 @@ description: Add client and server runtime configuration to your Next.js app. # Runtime Configuration -> Generally you'll want to use [build-time environment variables](/docs/api-reference/next.config.js/environment-variables.md) to provide your configuration. The reason for this is that runtime configuration adds rendering / initialization overhead and is incompatible with [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md). - -> Runtime configuration is not available when using the [`serverless` target](/docs/api-reference/next.config.js/build-target.md#serverless-target). +> Generally you'll want to use [build-time environment variables](/docs/basic-features/environment-variables.md) to provide your configuration. The reason for this is that runtime configuration adds rendering / initialization overhead and is incompatible with [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md). To add runtime configuration to your app open `next.config.js` and add the `publicRuntimeConfig` and `serverRuntimeConfig` configs: @@ -34,6 +32,7 @@ To get access to the runtime configs in your app use `next/config`, like so: ```jsx import getConfig from 'next/config' +import Image from 'next/image' // Only holds serverRuntimeConfig and publicRuntimeConfig const { serverRuntimeConfig, publicRuntimeConfig } = getConfig() @@ -45,7 +44,11 @@ console.log(publicRuntimeConfig.staticFolder) function MyImage() { return ( <div> - <img src={`${publicRuntimeConfig.staticFolder}/logo.png`} alt="logo" /> + <Image + src={`${publicRuntimeConfig.staticFolder}/logo.png`} + alt="logo" + layout="fill" + /> </div> ) } diff --git a/docs/api-reference/next.config.js/static-optimization-indicator.md b/docs/api-reference/next.config.js/static-optimization-indicator.md index 7ef296f9c36f9..f8c512d388a44 100644 --- a/docs/api-reference/next.config.js/static-optimization-indicator.md +++ b/docs/api-reference/next.config.js/static-optimization-indicator.md @@ -4,6 +4,8 @@ description: Optimized pages include an indicator to let you know if it's being # Static Optimization Indicator +> **Note:** This indicator was removed in Next.js version 10.0.1. We recommend upgrading to the latest version of Next.js. + When a page qualifies for [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) we show an indicator to let you know. This is helpful since automatic static optimization can be very beneficial and knowing immediately in development if the page qualifies can be useful. @@ -17,19 +19,3 @@ module.exports = { }, } ``` - -## Related - -<div class="card"> - <a href="/docs/api-reference/next.config.js/introduction.md"> - <b>Introduction to next.config.js:</b> - <small>Learn more about the configuration file used by Next.js.</small> - </a> -</div> - -<div class="card"> - <a href="/docs/advanced-features/automatic-static-optimization.md"> - <b>Automatic Static Optimization:</b> - <small>Next.js automatically optimizes your app to be static HTML whenever possible. Learn how it works here.</small> - </a> -</div> diff --git a/docs/api-reference/next.config.js/trailing-slash.md b/docs/api-reference/next.config.js/trailing-slash.md new file mode 100644 index 0000000000000..ee2757f872ccc --- /dev/null +++ b/docs/api-reference/next.config.js/trailing-slash.md @@ -0,0 +1,35 @@ +--- +description: Configure Next.js pages to resolve with or without a trailing slash. +--- + +# Trailing Slash + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| -------- | --------------------- | +| `v9.5.0` | Trailing Slash added. | + +</details> + +By default Next.js will redirect urls with trailing slashes to their counterpart without a trailing slash. For example `/about/` will redirect to `/about`. You can configure this behavior to act the opposite way, where urls without trailing slashes are redirected to their counterparts with trailing slashes. + +Open `next.config.js` and add the `trailingSlash` config: + +```js +module.exports = { + trailingSlash: true, +} +``` + +With this option set, urls like `/about` will redirect to `/about/`. + +## Related + +<div class="card"> + <a href="/docs/api-reference/next.config.js/introduction.md"> + <b>Introduction to next.config.js:</b> + <small>Learn more about the configuration file used by Next.js.</small> + </a> +</div> diff --git a/docs/api-reference/next/amp.md b/docs/api-reference/next/amp.md index 2e58efb82dcee..6cc212791168d 100644 --- a/docs/api-reference/next/amp.md +++ b/docs/api-reference/next/amp.md @@ -11,7 +11,7 @@ description: Enable AMP in a page, and control the way Next.js adds AMP to the p </ul> </details> -> AMP support is one of our advanced features, you can read more about it [here](/docs/advanced-features/amp-support/introduction.md). +> AMP support is one of our advanced features, you can [read more about AMP here](/docs/advanced-features/amp-support/introduction.md). To enable AMP, add the following config to your page: @@ -44,7 +44,7 @@ The page above is an AMP-only page, which means: - The page has no Next.js or React client-side runtime - The page is automatically optimized with [AMP Optimizer](https://github.com/ampproject/amp-toolbox/tree/master/packages/optimizer), an optimizer that applies the same transformations as AMP caches (improves performance by up to 42%) -- The page has an user-accessible (optimized) version of the page and a search-engine indexable (unoptimized) version of the page +- The page has a user-accessible (optimized) version of the page and a search-engine indexable (unoptimized) version of the page ## Hybrid AMP Page diff --git a/docs/api-reference/next/head.md b/docs/api-reference/next/head.md index b642c18cd5761..63010ff4e0643 100644 --- a/docs/api-reference/next/head.md +++ b/docs/api-reference/next/head.md @@ -61,3 +61,5 @@ In this case only the second `<meta property="og:title" />` is rendered. `meta` `title`, `meta` or any other elements (e.g. `script`) need to be contained as **direct** children of the `Head` element, or wrapped into maximum one level of `<React.Fragment>` or arrays—otherwise the tags won't be correctly picked up on client-side navigations. + +> We recommend using [next/script](/docs/basic-features/script.md) in your component instead of manually creating a `<script>` in `next/head`. diff --git a/docs/api-reference/next/image.md b/docs/api-reference/next/image.md new file mode 100644 index 0000000000000..b093a8d1598b9 --- /dev/null +++ b/docs/api-reference/next/image.md @@ -0,0 +1,280 @@ +--- +description: Enable Image Optimization with the built-in Image component. +--- + +# next/image + +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/image-component">Image Component</a></li> + </ul> +</details> + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ------------------------------------------------------------------------------------------------- | +| `v11.1.0` | `onLoadingComplete` and `lazyBoundary` props added. | +| `v11.0.0` | `src` prop support for static import.<br/>`placeholder` prop added.<br/>`blurDataURL` prop added. | +| `v10.0.5` | `loader` prop added. | +| `v10.0.1` | `layout` prop added. | +| `v10.0.0` | `next/image` introduced. | + +</details> + +> Before moving forward, we recommend you to read +> [Image Optimization](/docs/basic-features/image-optimization.md) first. + +Image Optimization can be enabled via the `<Image />` component exported by +`next/image`. + +## Usage + +For an example, consider a project with the following files: + +- `pages/index.js` +- `public/me.png` + +We can serve an optimized image like so: + +```jsx +import Image from 'next/image' +import profilePic from '../public/me.png' + +function Home() { + return ( + <> + <h1>My Homepage</h1> + <Image src={profilePic} alt="Picture of the author" /> + <p>Welcome to my homepage!</p> + </> + ) +} + +export default Home +``` + +## Required Props + +The `<Image />` component requires the following properties. + +### src + +Required and must be one of the following: + +1. A statically imported image file, as in the example code above, or +2. A path string. This can be either an absolute external URL, + or an internal path depending on the [loader](#loader). + +When using an external URL, you must add it to +[domains](/docs/basic-features/image-optimization.md#domains) in +`next.config.js`. + +### width + +The width of the image, in pixels. Must be an integer without a unit. + +Required, except for statically imported images, or those with [`layout="fill"`](#layout). + +### height + +The height of the image, in pixels. Must be an integer without a unit. + +Required, except for statically imported images, or those with [`layout="fill"`](#layout). + +## Optional Props + +The `<Image />` component optionally accepts the following properties. + +### layout + +The layout behavior of the image as the viewport changes size. + +| `layout` | Behavior | `srcSet` | `sizes` | +| --------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `intrinsic` (default) | Scale *down* to fit width of container, up to image size | `1x`, `2x` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes)) | N/A | +| `fixed` | Sized to `width` and `height` exactly | `1x`, `2x` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes)) | N/A | +| `responsive` | Scale to fit width of container | `640w`, `750w`, ... `2048w`, `3840w` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes) and [deviceSizes](/docs/basic-features/image-optimization.md#device-sizes)) | `100vw` | +| `fill` | Grow in X and Y axes to fill container | `640w`, `750w`, ... `2048w`, `3840w` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes) and [deviceSizes](/docs/basic-features/image-optimization.md#device-sizes)) | `100vw` | + +- [Demo the `intrinsic` layout (default)](https://image-component.nextjs.gallery/layout-intrinsic) + - When `intrinsic`, the image will scale the dimensions down for smaller viewports, but maintain the original dimensions for larger viewports. +- [Demo the `fixed` layout](https://image-component.nextjs.gallery/layout-fixed) + - When `fixed`, the image dimensions will not change as the viewport changes (no responsiveness) similar to the native `img` element. +- [Demo the `responsive` layout](https://image-component.nextjs.gallery/layout-responsive) + - When `responsive`, the image will scale the dimensions down for smaller viewports and scale up for larger viewports. + - Ensure the parent element uses `display: block` in their stylesheet. +- [Demo the `fill` layout](https://image-component.nextjs.gallery/layout-fill) + - When `fill`, the image will stretch both width and height to the dimensions of the parent element, provided the parent element is relative. + - This is usually paired with the [`objectFit`](#objectFit) property. + - Ensure the parent element has `position: relative` in their stylesheet. +- [Demo background image](https://image-component.nextjs.gallery/background) + +### loader + +A custom function used to resolve URLs. Defaults to [`images` object in `next.config.js`](/docs/basic-features/image-optimization.md#loader). + +`loader` is a function returning a string, given the following parameters: + +- [`src`](#src) +- [`width`](#width) +- [`quality`](#quality) + +```js +import Image from 'next/image' + +const myLoader = ({ src, width, quality }) => { + return `https://example.com/${src}?w=${width}&q=${quality || 75}` +} + +const MyImage = (props) => { + return ( + <Image + loader={myLoader} + src="me.png" + alt="Picture of the author" + width={500} + height={500} + /> + ) +} +``` + +### sizes + +A string mapping media queries to device sizes. Defaults to `100vw`. + +We recommend setting `sizes` when using `layout="responsive"` or `layout="fill"` and your image will **not** be the same width as the viewport. + +[Learn more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes). + +### quality + +The quality of the optimized image, an integer between `1` and `100` where `100` +is the best quality. Defaults to `75`. + +### priority + +When true, the image will be considered high priority and +[preload](https://web.dev/preload-responsive-images/). + +Should only be used when the image is visible above the fold. Defaults to +`false`. + +### placeholder + +A placeholder to use while the image is loading, possible values are `blur` or `empty`. Defaults to `empty`. + +When `blur`, the [`blurDataURL`](#blurdataurl) property will be used as the placeholder. If `src` is an object from a static import and the imported image is jpg, png, or webp, then `blurDataURL` will automatically be populated. + +For dynamic images, you must provide the [`blurDataURL`](#blurdataurl) property. Solutions such as [Plaiceholder](https://github.com/joe-bell/plaiceholder) can help with `base64` generation. + +When `empty`, there will be no placeholder while the image is loading, only empty space. + +Try it out: + +- [Demo the `blur` placeholder](https://image-component.nextjs.gallery/placeholder) +- [Demo the shimmer effect with `blurDataURL` prop](https://image-component.nextjs.gallery/shimmer) + +## Advanced Props + +In some cases, you may need more advanced usage. The `<Image />` component +optionally accepts the following advanced properties. + +### objectFit + +The image fit when using `layout="fill"`. + +[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) + +### objectPosition + +The image position when using `layout="fill"`. + +[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) + +### onLoadingComplete + +A callback function that is invoked once the image is completely loaded and the [placeholder](#placeholder) has been removed. + +The `onLoadingComplete` function accepts one parameter, an object with the following properties: + +- [`naturalWidth`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/naturalWidth) +- [`naturalHeight`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/naturalHeight) + +### loading + +> **Attention**: This property is only meant for advanced usage. Switching an +> image to load with `eager` will normally **hurt performance**. +> +> We recommend using the [`priority`](#priority) property instead, which +> properly loads the image eagerly for nearly all use cases. + +The loading behavior of the image. Defaults to `lazy`. + +When `lazy`, defer loading the image until it reaches a calculated distance from +the viewport. + +When `eager`, load the image immediately. + +[Learn more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading) + +### blurDataURL + +A [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) to +be used as a placeholder image before the `src` image successfully loads. Only takes effect when combined +with [`placeholder="blur"`](#placeholder). + +Must be a base64-encoded image. It will be enlarged and blurred, so a very small image (10px or +less) is recommended. Including larger images as placeholders may harm your application performance. + +Try it out: + +- [Demo the default `blurDataURL` prop](https://image-component.nextjs.gallery/placeholder) +- [Demo the shimmer effect with `blurDataURL` prop](https://image-component.nextjs.gallery/shimmer) + +You can also [generate a solid color Data URL](https://png-pixel.com) to match the image. + +### lazyBoundary + +A string (with similar syntax to the margin property) that acts as the bounding box used to detect the intersection of the viewport with the image and trigger lazy [loading](#loading). Defaults to `"200px"`. + +[Learn more](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) + +### unoptimized + +When true, the source image will be served as-is instead of changing quality, +size, or format. Defaults to `false`. + +## Other Props + +Other properties on the `<Image />` component will be passed to the underlying +`img` element with the exception of the following: + +- `style`. Use `className` instead. +- `srcSet`. Use + [Device Sizes](/docs/basic-features/image-optimization.md#device-sizes) + instead. +- `ref`. Use [`onLoadingComplete`](#onloadingcomplete) instead. +- `decoding`. It is always `"async"`. + +## Styling + +`next/image` wraps the `img` element with other `div` elements to maintain the aspect ratio of the image and prevent [Cumulative Layout Shift](https://vercel.com/blog/core-web-vitals#cumulative-layout-shift). + +To add styles to the underlying `img` element, pass the `className` prop to the `<Image />` component. Then, use Next.js' [built-in CSS support](/docs/basic-features/built-in-css-support.md) to add rules to that class. + +**Note:** If using [`layout="fill"`](/docs/api-reference/next/image.md#layout), ensure the parent element uses `position: relative`. + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/basic-features/image-optimization.md"> + <b>Image Optimization</b> + <small>See how to configure domains and loaders.</small> + </a> +</div> diff --git a/docs/api-reference/next/link.md b/docs/api-reference/next/link.md index ed670bc9936c2..a5272331968a4 100644 --- a/docs/api-reference/next/link.md +++ b/docs/api-reference/next/link.md @@ -8,6 +8,7 @@ description: Enable client-side transitions between routes with the built-in Lin <summary><b>Examples</b></summary> <ul> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/hello-world">Hello World</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/active-class-name">Active className on Link</a></li> </ul> </details> @@ -15,7 +16,13 @@ description: Enable client-side transitions between routes with the built-in Lin Client-side transitions between routes can be enabled via the `Link` component exported by `next/link`. -An example of linking to `/` and `/about`: +For an example, consider a `pages` directory with the following files: + +- `pages/index.js` +- `pages/about.js` +- `pages/blog/[slug].js` + +We can have a link to each of these pages like so: ```jsx import Link from 'next/link' @@ -33,6 +40,11 @@ function Home() { <a>About Us</a> </Link> </li> + <li> + <Link href="/blog/hello-world"> + <a>Blog Post</a> + </Link> + </li> </ul> ) } @@ -42,37 +54,39 @@ export default Home `Link` accepts the following props: -- `href` - The path inside `pages` directory. This is the only required prop -- `as` - The path that will be rendered in the browser URL bar. Used for dynamic routes -- [`passHref`](#forcing-Link-to-expose-href-to-its-child) - Forces `Link` to send the `href` property to its child. Defaults to `false` -- `prefetch` - Prefetch the page in the background. Defaults to `true`. Any `<Link />` that is in the viewport (initially or through scroll) will be preloaded. Pages using [Static Generation](/docs/basic-features/data-fetching#getstaticprops-static-generation) will preload `JSON` files with the data for faster page transitions. +- `href` - The path or URL to navigate to. This is the only required prop +- `as` - Optional decorator for the path that will be shown in the browser URL bar. Before Next.js 9.5.3 this was used for dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked +- [`passHref`](#if-the-child-is-a-custom-component-that-wraps-an-a-tag) - Forces `Link` to send the `href` property to its child. Defaults to `false` +- `prefetch` - Prefetch the page in the background. Defaults to `true`. Any `<Link />` that is in the viewport (initially or through scroll) will be preloaded. Prefetch can be disabled by passing `prefetch={false}`. When `prefetch` is set to `false`, prefetching will still occur on hover. Pages using [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) will preload `JSON` files with the data for faster page transitions. Prefetching is only enabled in production. - [`replace`](#replace-the-url-instead-of-push) - Replace the current `history` state instead of adding a new url into the stack. Defaults to `false` - [`scroll`](#disable-scrolling-to-the-top-of-the-page) - Scroll to the top of the page after a navigation. Defaults to `true` - [`shallow`](/docs/routing/shallow-routing.md) - Update the path of the current page without rerunning [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation), [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) or [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md). Defaults to `false` +- `locale` - The active locale is automatically prepended. `locale` allows for providing a different locale. When `false` `href` has to include the locale as the default behavior is disabled. -External URLs, and any links that don't require a route navigation using `/pages`, don't need to be handled with `Link`; use the anchor tag for such cases instead. +## If the route has dynamic segments -## Dynamic routes +There is nothing to do when linking to a [dynamic route](/docs/routing/dynamic-routes.md), including [catch all routes](/docs/routing/dynamic-routes.md#catch-all-routes), since Next.js 9.5.3 (for older versions check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes)). However, it can become quite common and handy to use [interpolation](/docs/routing/introduction.md#linking-to-dynamic-paths) or an [URL Object](#with-url-object) to generate the link. -A `Link` to a dynamic route is a combination of the `href` and `as` props. A link to the page `pages/post/[pid].js` will look like this: +For example, the dynamic route `pages/blog/[slug].js` will match the following link: ```jsx -<Link href="/post/[pid]" as="/post/abc"> - <a>First Post</a> -</Link> -``` - -`href` is a file system path used by the page and it shouldn't change at runtime. `as` on the other hand, will be dynamic most of the time according to your needs. Here's an example of how to create a list of links: +import Link from 'next/link' -```jsx -const pids = ['id1', 'id2', 'id3'] -{ - pids.map((pid) => ( - <Link href="/post/[pid]" as={`/post/${pid}`}> - <a>Post {pid}</a> - </Link> - )) +function Posts({ posts }) { + return ( + <ul> + {posts.map((post) => ( + <li key={post.id}> + <Link href={`/blog/${encodeURIComponent(post.slug)}`}> + <a>{post.title}</a> + </Link> + </li> + ))} + </ul> + ) } + +export default Posts ``` ## If the child is a custom component that wraps an `<a>` tag @@ -100,7 +114,8 @@ function NavLink({ href, name }) { export default NavLink ``` -> **Note**: If you’re using [emotion](https://emotion.sh/)’s JSX pragma feature (`@jsx jsx`), you must use `passHref` even if you use an `<a>` tag directly. +- If you’re using [emotion](https://emotion.sh/)’s JSX pragma feature (`@jsx jsx`), you must use `passHref` even if you use an `<a>` tag directly. +- The component should support `onClick` property to trigger navigation correctly ## If the child is a function component @@ -132,25 +147,47 @@ export default Home ## With URL Object -`Link` can also receive an URL object and it will automatically format it to create the URL string. Here's how to do it: +`Link` can also receive a URL object and it will automatically format it to create the URL string. Here's how to do it: ```jsx import Link from 'next/link' function Home() { return ( - <div> - <Link href={{ pathname: '/about', query: { name: 'test' } }}> - <a>About us</a> - </Link> - </div> + <ul> + <li> + <Link + href={{ + pathname: '/about', + query: { name: 'test' }, + }} + > + <a>About us</a> + </Link> + </li> + <li> + <Link + href={{ + pathname: '/blog/[slug]', + query: { slug: 'my-post' }, + }} + > + <a>Blog Post</a> + </Link> + </li> + </ul> ) } export default Home ``` -The above example will be a link to `/about?name=test`. You can use every property as defined in the [Node.js URL module documentation](https://nodejs.org/api/url.html#url_url_strings_and_url_objects). +The above example has a link to: + +- A predefined route: `/about?name=test` +- A [dynamic route](/docs/routing/dynamic-routes.md): `/blog/my-post` + +You can use every property as defined in the [Node.js URL module documentation](https://nodejs.org/api/url.html#url_url_strings_and_url_objects). ## Replace the URL instead of push @@ -162,18 +199,6 @@ The default behavior of the `Link` component is to `push` a new URL into the `hi </Link> ``` -## Using a component that supports `onClick` - -`Link` supports any component that supports the `onClick` event, in the case you don't provide an `<a>` tag, consider the following example: - -```jsx -<Link href="/about"> - <img src="/static/image.png" alt="image" /> -</Link> -``` - -The child of `Link` is `<img>` instead of `<a>`. `Link` will send the `onClick` property to `<img>` but won't pass the `href` property. - ## Disable scrolling to the top of the page The default behavior of `Link` is to scroll to the top of the page. When there is a hash defined it will scroll to the specific id, like a normal `<a>` tag. To prevent scrolling to the top / hash `scroll={false}` can be added to `Link`: diff --git a/docs/api-reference/next/router.md b/docs/api-reference/next/router.md index b22afe6137ca8..60aa34f7f7092 100644 --- a/docs/api-reference/next/router.md +++ b/docs/api-reference/next/router.md @@ -17,7 +17,7 @@ function ActiveLink({ children, href }) { const router = useRouter() const style = { marginRight: 10, - color: router.pathname === href ? 'red' : 'black', + color: router.asPath === href ? 'red' : 'black', } const handleClick = (e) => { @@ -37,35 +37,25 @@ export default ActiveLink > `useRouter` is a [React Hook](https://reactjs.org/docs/hooks-intro.html), meaning it cannot be used with classes. You can either use [withRouter](#withRouter) or wrap your class in a function component. -### router object +## `router` object The following is the definition of the `router` object returned by both [`useRouter`](#useRouter) and [`withRouter`](#withRouter): -- `pathname`: `String` - Current route. That is the path of the page in `/pages` +- `pathname`: `String` - Current route. That is the path of the page in `/pages`, the configured `basePath` or `locale` is not included. - `query`: `Object` - The query string parsed to an object. It will be an empty object during prerendering if the page doesn't have [data fetching requirements](/docs/basic-features/data-fetching.md). Defaults to `{}` -- `asPath`: `String` - Actual path (including the query) shown in the browser +- `asPath`: `String` - The path (including the query) shown in the browser without the configured `basePath` or `locale`. +- `isFallback`: `boolean` - Whether the current page is in [fallback mode](/docs/basic-features/data-fetching.md#fallback-pages). +- `basePath`: `String` - The active [basePath](/docs/api-reference/next.config.js/basepath.md) (if enabled). +- `locale`: `String` - The active locale (if enabled). +- `locales`: `String[]` - All supported locales (if enabled). +- `defaultLocale`: `String` - The current default locale (if enabled). +- `domainLocales`: `Array<{domain, defaultLocale, locales}>` - Any configured domain locales. +- `isReady`: `boolean` - Whether the router fields are updated client-side and ready for use. Should only be used inside of `useEffect` methods and not for conditionally rendering on the server. +- `isPreview`: `boolean` - Whether the application is currently in [preview mode](/docs/advanced-features/preview-mode.md). -Additionally, the [`Router API`](#router-api) is also included inside the object. +Additionally, the following methods are also included inside `router`: -## withRouter - -If [`useRouter`](#useRouter) is not the best fit for you, `withRouter` can also add the same [`router` object](#router-object) to any component, here's how to use it: - -```jsx -import { withRouter } from 'next/router' - -function Page({ router }) { - return <p>{router.pathname}</p> -} - -export default withRouter(Page) -``` - -## Router API - -The API of `Router`, exported by `next/router`, is defined below. - -### Router.push +### router.push <details> <summary><b>Examples</b></summary> @@ -77,95 +67,143 @@ The API of `Router`, exported by `next/router`, is defined below. Handles client-side transitions, this method is useful for cases where [`next/link`](/docs/api-reference/next/link.md) is not enough. ```jsx -import Router from 'next/router' - -Router.push(url, as, options) +router.push(url, as, options) ``` -- `url` - The URL to navigate to. This is usually the name of a `page` -- `as` - Optional decorator for the URL that will be shown in the browser. Defaults to `url` +- `url` - The URL to navigate to +- `as` - Optional decorator for the URL that will be shown in the browser. Before Next.js 9.5.3 this was used for dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked - `options` - Optional object with the following configuration options: + - `scroll` - Optional boolean, controls scrolling to the top of the page after navigation. Defaults to `true` - [`shallow`](/docs/routing/shallow-routing.md): Update the path of the current page without rerunning [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation), [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) or [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md). Defaults to `false` + - `locale` - Optional string, indicates locale of the new page -> You don't need to use `Router` for external URLs, [window.location](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) is better suited for those cases. +> You don't need to use `router.push` for external URLs. [window.location](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) is better suited for those cases. #### Usage Navigating to `pages/about.js`, which is a predefined route: ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' -function Page() { - return <span onClick={() => Router.push('/about')}>Click me</span> +export default function Page() { + const router = useRouter() + + return ( + <button type="button" onClick={() => router.push('/about')}> + Click me + </button> + ) } ``` Navigating `pages/post/[pid].js`, which is a dynamic route: ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' + +export default function Page() { + const router = useRouter() -function Page() { return ( - <span onClick={() => Router.push('/post/[pid]', '/post/abc')}> + <button type="button" onClick={() => router.push('/post/abc')}> Click me - </span> + </button> ) } ``` -#### With URL object +> **Note:** When navigating to the same page in Next.js, the page's state **will not** be reset by default, as the top-level React component is the same. You can manually ensure the state is updated using `useEffect`. -You can use an URL object in the same way you can use it for [`next/link`](/docs/api-reference/next/link.md#with-url-object). Works for both the `url` and `as` parameters: +Redirecting the user to `pages/login.js`, useful for pages behind [authentication](/docs/authentication): ```jsx -import Router from 'next/router' +import { useEffect } from 'react' +import { useRouter } from 'next/router' + +// Here you would fetch and return the user +const useUser = () => ({ user: null, loading: false }) + +export default function Page() { + const { user, loading } = useUser() + const router = useRouter() + + useEffect(() => { + if (!(user || loading)) { + router.push('/login') + } + }, [user, loading]) -const handler = () => { - Router.push({ - pathname: '/about', - query: { name: 'Vercel' }, - }) + return <p>Redirecting...</p> } +``` + +#### With URL object + +You can use a URL object in the same way you can use it for [`next/link`](/docs/api-reference/next/link.md#with-url-object). Works for both the `url` and `as` parameters: + +```jsx +import { useRouter } from 'next/router' + +export default function ReadMore({ post }) { + const router = useRouter() -function ReadMore() { return ( - <div> - Click <span onClick={handler}>here</span> to read more - </div> + <button + type="button" + onClick={() => { + router.push({ + pathname: '/post/[pid]', + query: { pid: post.id }, + }) + }} + > + Click here to read more + </button> ) } +``` + +### router.replace -export default ReadMore +Similar to the `replace` prop in [`next/link`](/docs/api-reference/next/link.md), `router.replace` will prevent adding a new URL entry into the `history` stack. + +```jsx +router.replace(url, as, options) ``` -### Router.replace +- The API for `router.replace` is exactly the same as the API for [`router.push`](#router.push). -Similar to the `replace` prop in [`next/link`](/docs/api-reference/next/link.md), `Router.replace` will prevent adding a new URL entry into the `history` stack, take a look at the following example: +#### Usage + +Take a look at the following example: ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' -Router.replace('/home') -``` +export default function Page() { + const router = useRouter() -The API for `Router.replace` is exactly the same as that used for [`Router.push`](#router.push). + return ( + <button type="button" onClick={() => router.replace('/home')}> + Click me + </button> + ) +} +``` -### Router.prefetch +### router.prefetch Prefetch pages for faster client-side transitions. This method is only useful for navigations without [`next/link`](/docs/api-reference/next/link.md), as `next/link` takes care of prefetching pages automatically. > This is a production only feature. Next.js doesn't prefetch pages on development. ```jsx -import Router from 'next/router' - -Router.prefetch(url, as) +router.prefetch(url, as) ``` -- `url` - The path to a `page` inside the `pages` directory -- `as` - Optional decorator for `url`, used to prefetch [dynamic routes](/docs/routing/dynamic-routes). Defaults to `url` +- `url` - The URL to prefetch, that is, a path with a matching page +- `as` - Optional decorator for `url`. Before Next.js 9.5.3 this was used to prefetch dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked #### Usage @@ -173,9 +211,10 @@ Let's say you have a login page, and after a login, you redirect the user to the ```jsx import { useCallback, useEffect } from 'react' -import Router from 'next/router' +import { useRouter } from 'next/router' export default function Login() { + const router = useRouter() const handleSubmit = useCallback((e) => { e.preventDefault() @@ -187,13 +226,13 @@ export default function Login() { }), }).then((res) => { // Do a fast client-side transition to the already prefetched dashboard page - if (res.ok) Router.push('/dashboard') + if (res.ok) router.push('/dashboard') }) }, []) useEffect(() => { - // Prefetch the dashboard page as the user will go there after the login - Router.prefetch('/dashboard') + // Prefetch the dashboard page + router.prefetch('/dashboard') }, []) return ( @@ -205,57 +244,90 @@ export default function Login() { } ``` -### Router.beforePopState +### router.beforePopState In some cases (for example, if using a [Custom Server](/docs/advanced-features/custom-server.md)), you may wish to listen to [popstate](https://developer.mozilla.org/en-US/docs/Web/Events/popstate) and do something before the router acts on it. -You could use this to manipulate the request, or force a SSR refresh, as in the following example: - ```jsx -import Router from 'next/router' - -Router.beforePopState(({ url, as, options }) => { - // I only want to allow these two routes! - if (as !== '/' && as !== '/other') { - // Have SSR render bad routes as a 404. - window.location.href = as - return false - } - - return true -}) +router.beforePopState(cb) ``` -`Router.beforePopState(cb: () => boolean)` - - `cb` - The function to run on incoming `popstate` events. The function receives the state of the event as an object with the following props: - `url`: `String` - the route for the new state. This is usually the name of a `page` - `as`: `String` - the url that will be shown in the browser - - `options`: `Object` - Additional options sent by [Router.push](#router.push) + - `options`: `Object` - Additional options sent by [router.push](#router.push) + +If `cb` returns `false`, the Next.js router will not handle `popstate`, and you'll be responsible for handling it in that case. See [Disabling file-system routing](/docs/advanced-features/custom-server.md#disabling-file-system-routing). + +#### Usage -If the function you pass into `beforePopState` returns `false`, `Router` will not handle `popstate` and you'll be responsible for handling it, in that case. See [Disabling file-system routing](/docs/advanced-features/custom-server.md#disabling-file-system-routing). +You could use `beforePopState` to manipulate the request, or force a SSR refresh, as in the following example: -### Router.back +```jsx +import { useEffect } from 'react' +import { useRouter } from 'next/router' + +export default function Page() { + const router = useRouter() + + useEffect(() => { + router.beforePopState(({ url, as, options }) => { + // I only want to allow these two routes! + if (as !== '/' && as !== '/other') { + // Have SSR render bad routes as a 404. + window.location.href = as + return false + } + + return true + }) + }, []) + + return <p>Welcome to the page</p> +} +``` + +### router.back Navigate back in history. Equivalent to clicking the browser’s back button. It executes `window.history.back()`. +#### Usage + ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' + +export default function Page() { + const router = useRouter() -Router.back() + return ( + <button type="button" onClick={() => router.back()}> + Click here to go back + </button> + ) +} ``` -### Router.reload +### router.reload Reload the current URL. Equivalent to clicking the browser’s refresh button. It executes `window.location.reload()`. +#### Usage + ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' + +export default function Page() { + const router = useRouter() -Router.reload() + return ( + <button type="button" onClick={() => router.reload()}> + Click here to reload + </button> + ) +} ``` -### Router.events +### router.events <details> <summary><b>Examples</b></summary> @@ -264,63 +336,119 @@ Router.reload() </ul> </details> -You can listen to different events happening inside the Router. Here's a list of supported events: +You can listen to different events happening inside the Next.js Router. Here's a list of supported events: -- `routeChangeStart(url)` - Fires when a route starts to change -- `routeChangeComplete(url)` - Fires when a route changed completely -- `routeChangeError(err, url)` - Fires when there's an error when changing routes, or a route load is cancelled +- `routeChangeStart(url, { shallow })` - Fires when a route starts to change +- `routeChangeComplete(url, { shallow })` - Fires when a route changed completely +- `routeChangeError(err, url, { shallow })` - Fires when there's an error when changing routes, or a route load is cancelled - `err.cancelled` - Indicates if the navigation was cancelled -- `beforeHistoryChange(url)` - Fires just before changing the browser's history -- `hashChangeStart(url)` - Fires when the hash will change but not the page -- `hashChangeComplete(url)` - Fires when the hash has changed but not the page +- `beforeHistoryChange(url, { shallow })` - Fires before changing the browser's history +- `hashChangeStart(url, { shallow })` - Fires when the hash will change but not the page +- `hashChangeComplete(url, { shallow })` - Fires when the hash has changed but not the page + +> **Note:** Here `url` is the URL shown in the browser, including the [`basePath`](/docs/api-reference/next.config.js/basepath.md). -> Here `url` is the URL shown in the browser. If you call `Router.push(url, as)` (or similar), then the value of `url` will be `as`. +#### Usage -For example, to listen to the router event `routeChangeStart`, do the following: +For example, to listen to the router event `routeChangeStart`, open or create `pages/_app.js` and subscribe to the event, like so: ```jsx -import Router from 'next/router' +import { useEffect } from 'react' +import { useRouter } from 'next/router' -const handleRouteChange = (url) => { - console.log('App is changing to: ', url) -} +export default function MyApp({ Component, pageProps }) { + const router = useRouter() -Router.events.on('routeChangeStart', handleRouteChange) + useEffect(() => { + const handleRouteChange = (url, { shallow }) => { + console.log( + `App is changing to ${url} ${ + shallow ? 'with' : 'without' + } shallow routing` + ) + } + + router.events.on('routeChangeStart', handleRouteChange) + + // If the component is unmounted, unsubscribe + // from the event with the `off` method: + return () => { + router.events.off('routeChangeStart', handleRouteChange) + } + }, []) + + return <Component {...pageProps} /> +} ``` -If you no longer want to listen to the event, unsubscribe with the `off` method: +> We use a [Custom App](/docs/advanced-features/custom-app.md) (`pages/_app.js`) for this example to subscribe to the event because it's not unmounted on page navigations, but you can subscribe to router events on any component in your application. + +Router events should be registered when a component mounts ([useEffect](https://reactjs.org/docs/hooks-effect.html) or [componentDidMount](https://reactjs.org/docs/react-component.html#componentdidmount) / [componentWillUnmount](https://reactjs.org/docs/react-component.html#componentwillunmount)) or imperatively when an event happens. + +If a route load is cancelled (for example, by clicking two links rapidly in succession), `routeChangeError` will fire. And the passed `err` will contain a `cancelled` property set to `true`, as in the following example: ```jsx -import Router from 'next/router' +import { useEffect } from 'react' +import { useRouter } from 'next/router' -Router.events.off('routeChangeStart', handleRouteChange) +export default function MyApp({ Component, pageProps }) { + const router = useRouter() + + useEffect(() => { + const handleRouteChangeError = (err, url) => { + if (err.cancelled) { + console.log(`Route to ${url} was cancelled!`) + } + } + + router.events.on('routeChangeError', handleRouteChangeError) + + // If the component is unmounted, unsubscribe + // from the event with the `off` method: + return () => { + router.events.off('routeChangeError', handleRouteChangeError) + } + }, []) + + return <Component {...pageProps} /> +} ``` -If a route load is cancelled (for example, by clicking two links rapidly in succession), `routeChangeError` will fire. And the passed `err` will contain a `cancelled` property set to `true`, as in the following example: +## withRouter + +If [`useRouter`](#useRouter) is not the best fit for you, `withRouter` can also add the same [`router` object](#router-object) to any component. + +### Usage ```jsx -import Router from 'next/router' +import { withRouter } from 'next/router' -Router.events.on('routeChangeError', (err, url) => { - if (err.cancelled) { - console.log(`Route to ${url} was cancelled!`) - } -}) +function Page({ router }) { + return <p>{router.pathname}</p> +} + +export default withRouter(Page) ``` -Router events should be registered when a component mounts ([useEffect](https://reactjs.org/docs/hooks-effect.html) or [componentDidMount](https://reactjs.org/docs/react-component.html#componentdidmount) / [componentWillUnmount](https://reactjs.org/docs/react-component.html#componentwillunmount)) or imperatively when an event happens, as in the following example: +### TypeScript -```jsx -import Router from 'next/router' +To use class components with `withRouter`, the component needs to accept a router prop: -useEffect(() => { - const handleRouteChange = (url) => { - console.log('App is changing to: ', url) - } +```tsx +import React from 'react' +import { withRouter, NextRouter } from 'next/router' + +interface WithRouterProps { + router: NextRouter +} + +interface MyComponentProps extends WithRouterProps {} - Router.events.on('routeChangeStart', handleRouteChange) - return () => { - Router.events.off('routeChangeStart', handleRouteChange) +class MyComponent extends React.Component<MyComponentProps> { + render() { + return <p>{this.props.router.pathname}</p> } -}, []) +} + +export default withRouter(MyComponent) ``` diff --git a/docs/api-routes/api-middlewares.md b/docs/api-routes/api-middlewares.md index 78c75b95cf0f6..5710da1593df2 100644 --- a/docs/api-routes/api-middlewares.md +++ b/docs/api-routes/api-middlewares.md @@ -116,3 +116,71 @@ export default handler ``` > Go to the [API Routes with CORS](https://github.com/vercel/next.js/tree/canary/examples/api-routes-cors) example to see the finished app + +## Extending the `req`/`res` objects with TypeScript + +For better type-safety, it is not recommended to extend the `req` and `res` objects. Instead, use functions to work with them: + +```ts +// utils/cookies.ts + +import { serialize, CookieSerializeOptions } from 'cookie' +import { NextApiResponse } from 'next' + +/** + * This sets `cookie` using the `res` object + */ + +export const setCookie = ( + res: NextApiResponse, + name: string, + value: unknown, + options: CookieSerializeOptions = {} +) => { + const stringValue = + typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value) + + if ('maxAge' in options) { + options.expires = new Date(Date.now() + options.maxAge) + options.maxAge /= 1000 + } + + res.setHeader('Set-Cookie', serialize(name, String(stringValue), options)) +} + +// pages/api/cookies.ts + +import { NextApiRequest, NextApiResponse } from 'next' +import { setCookie } from '../../utils/cookies' + +const handler = (req: NextApiRequest, res: NextApiResponse) => { + // Calling our pure function using the `res` object, it will add the `set-cookie` header + setCookie(res, 'Next.js', 'api-middleware!') + // Return the `set-cookie` header so we can display it in the browser and show that it works! + res.end(res.getHeader('Set-Cookie')) +} + +export default handler +``` + +If you can't avoid these objects from being extended, you have to create your own type to include the extra properties: + +```ts +// pages/api/foo.ts + +import { NextApiRequest, NextApiResponse } from 'next' +import { withFoo } from 'external-lib-foo' + +type NextApiRequestWithFoo = NextApiRequest & { + foo: (bar: string) => void +} + +const handler = (req: NextApiRequestWithFoo, res: NextApiResponse) => { + req.foo('bar') // we can now use `req.foo` without type errors + res.end('ok') +} + +export default withFoo(handler) +``` + +Keep in mind this is not safe since the code will still compile even if you remove `withFoo()` from the export. diff --git a/docs/api-routes/dynamic-api-routes.md b/docs/api-routes/dynamic-api-routes.md index c8204455d1d87..8bbf65affef99 100644 --- a/docs/api-routes/dynamic-api-routes.md +++ b/docs/api-routes/dynamic-api-routes.md @@ -16,11 +16,8 @@ API routes support [dynamic routes](/docs/routing/dynamic-routes.md), and follow For example, the API route `pages/api/post/[pid].js` has the following code: ```js -export default (req, res) => { - const { - query: { pid }, - } = req - +export default function handler(req, res) { + const { pid } = req.query res.end(`Post: ${pid}`) } ``` @@ -31,7 +28,7 @@ Now, a request to `/api/post/abc` will respond with the text: `Post: abc`. A very common RESTful pattern is to set up routes like this: -- `GET api/posts/` - gets a list of posts, probably paginated +- `GET api/posts` - gets a list of posts, probably paginated - `GET api/posts/12345` - gets post id 12345 We can model this in two ways: @@ -40,11 +37,10 @@ We can model this in two ways: - `/api/posts.js` - `/api/posts/[postId].js` - Option 2: - - `/api/posts/index.js` - `/api/posts/[postId].js` -Both are equivalent. A third option of only using `/api/posts/[postId].js` is not valid because Dynamic Routes (including Catch-all routes - see below) do not have an `undefined` state and `GET api/posts/` will not match `/api/posts/[postId].js` under any circumstances. +Both are equivalent. A third option of only using `/api/posts/[postId].js` is not valid because Dynamic Routes (including Catch-all routes - see below) do not have an `undefined` state and `GET api/posts` will not match `/api/posts/[postId].js` under any circumstances. ### Catch all API routes @@ -69,11 +65,8 @@ And in the case of `/api/post/a/b`, and any other matching path, new parameters An API route for `pages/api/post/[...slug].js` could look like this: ```js -export default (req, res) => { - const { - query: { slug }, - } = req - +export default function handler(req, res) { + const { slug } = req.query res.end(`Post: ${slug.join(', ')}`) } ``` @@ -86,6 +79,8 @@ Catch all routes can be made optional by including the parameter in double brack For example, `pages/api/post/[[...slug]].js` will match `/api/post`, `/api/post/a`, `/api/post/a/b`, and so on. +The main difference between catch all and optional catch all routes is that with optional, the route without the parameter is also matched (`/api/post` in the example above). + The `query` objects are as follows: ```json diff --git a/docs/api-routes/introduction.md b/docs/api-routes/introduction.md index eac86c6ab3db0..4274f7d0f7b2b 100644 --- a/docs/api-routes/introduction.md +++ b/docs/api-routes/introduction.md @@ -15,29 +15,27 @@ description: Next.js supports API Routes, which allow you to build your API with </ul> </details> -API routes provide a straightforward solution to build your **API** with Next.js. +API routes provide a solution to build your **API** with Next.js. -Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a `page`. +Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a `page`. They are server-side only bundles and won't increase your client-side bundle size. -For example, the following API route `pages/api/user.js` handles a `json` response: +For example, the following API route `pages/api/user.js` returns a `json` response with a status code of `200`: ```js -export default (req, res) => { - res.statusCode = 200 - res.setHeader('Content-Type', 'application/json') - res.end(JSON.stringify({ name: 'John Doe' })) +export default function handler(req, res) { + res.status(200).json({ name: 'John Doe' }) } ``` -For an API route to work, you need to export as default a function (a.k.a **request handler**), which then receives the following parameters: +For an API route to work, you need to export a function as default (a.k.a **request handler**), which then receives the following parameters: -- `req`: An instance of [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage), plus some pre-built middlewares you can see [here](/docs/api-routes/api-middlewares.md) -- `res`: An instance of [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse), plus some helper functions you can see [here](/docs/api-routes/response-helpers.md) +- `req`: An instance of [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage), plus some [pre-built middlewares](/docs/api-routes/api-middlewares.md) +- `res`: An instance of [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse), plus some [helper functions](/docs/api-routes/response-helpers.md) To handle different HTTP methods in an API route, you can use `req.method` in your request handler, like so: ```js -export default (req, res) => { +export default function handler(req, res) { if (req.method === 'POST') { // Process a POST request } else { @@ -48,9 +46,17 @@ export default (req, res) => { To fetch API endpoints, take a look into any of the examples at the start of this section. -> API Routes [do not specify CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), meaning they are **same-origin only** by default. You can customize such behavior by wrapping the request handler with the [cors middleware](/docs/api-routes/api-middlewares.md#connectexpress-middleware-support). +## Use Cases -> API Routes do not increase your client-side bundle size. They are server-side only bundles. +For new projects, you can build your entire API with API Routes. If you have an existing API, you do not need to forward calls to the API through an API Route. Some other use cases for API Routes are: + +- Masking the URL of an external service (e.g. `/api/secret` instead of `https://company.com/secret-url`) +- Using [Environment Variables](/docs/basic-features/environment-variables.md) on the server to securely access external services. + +## Caveats + +- API Routes [do not specify CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), meaning they are **same-origin only** by default. You can customize such behavior by wrapping the request handler with the [CORS middleware](/docs/api-routes/api-middlewares.md#connectexpress-middleware-support). +- API Routes can't be used with [`next export`](/docs/advanced-features/static-html-export.md) ## Related diff --git a/docs/api-routes/response-helpers.md b/docs/api-routes/response-helpers.md index 93fd1623f03ea..0418b2362e446 100644 --- a/docs/api-routes/response-helpers.md +++ b/docs/api-routes/response-helpers.md @@ -15,7 +15,7 @@ description: API Routes include a set of Express.js-like methods for the respons The response (`res`) includes a set of Express.js-like methods to improve the developer experience and increase the speed of creating new API endpoints, take a look at the following example: ```js -export default (req, res) => { +export default function handler(req, res) { res.status(200).json({ name: 'Next.js' }) } ``` @@ -23,5 +23,8 @@ export default (req, res) => { The included helpers are: - `res.status(code)` - A function to set the status code. `code` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) -- `res.json(json)` - Sends a JSON response. `json` must be a valid JSON object +- `res.json(body)` - Sends a JSON response. `body` must be a [serialiazable object](https://developer.mozilla.org/en-US/docs/Glossary/Serialization) - `res.send(body)` - Sends the HTTP response. `body` can be a `string`, an `object` or a `Buffer` +- `res.redirect([status,] path)` - Redirects to a specified path or URL. `status` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). If not specified, `status` defaults to "307" "Temporary redirect". + +To view an example using types, check out the [TypeScript documentation](/docs/basic-features/typescript.md#api-routes). diff --git a/docs/authentication.md b/docs/authentication.md new file mode 100644 index 0000000000000..f0170d95cdc15 --- /dev/null +++ b/docs/authentication.md @@ -0,0 +1,163 @@ +--- +description: Learn about authentication patterns in Next.js apps and explore a few examples. +--- + +# Authentication + +Authentication verifies who a user is, while authorization controls what a user can access. Next.js supports multiple authentication patterns, each designed for different use cases. This page will go through each case so that you can choose based on your constraints. + +## Authentication Patterns + +The first step to identifying which authentication pattern you need is understanding the [data-fetching strategy](/docs/basic-features/data-fetching.md) you want. We can then determine which authentication providers support this strategy. There are two main patterns: + +- Use [static generation](/docs/basic-features/pages.md#static-generation-recommended) to server-render a loading state, followed by fetching user data client-side. +- Fetch user data [server-side](/docs/basic-features/pages.md#server-side-rendering) to eliminate a flash of unauthenticated content. + +### Authenticating Statically Generated Pages + +Next.js automatically determines that a page is static if there are no blocking data requirements. This means the absence of [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) and `getInitialProps` in the page. Instead, your page can render a loading state from the server, followed by fetching the user client-side. + +One advantage of this pattern is it allows pages to be served from a global CDN and preloaded using [`next/link`](/docs/api-reference/next/link.md). In practice, this results in a faster TTI ([Time to Interactive](https://web.dev/interactive/)). + +Let's look at an example for a profile page. This will initially render a loading skeleton. Once the request for a user has finished, it will show the user's name: + +```jsx +// pages/profile.js + +import useUser from '../lib/useUser' +import Layout from '../components/Layout' + +const Profile = () => { + // Fetch the user client-side + const { user } = useUser({ redirectTo: '/login' }) + + // Server-render loading state + if (!user || user.isLoggedIn === false) { + return <Layout>Loading...</Layout> + } + + // Once the user request finishes, show the user + return ( + <Layout> + <h1>Your Profile</h1> + <pre>{JSON.stringify(user, null, 2)}</pre> + </Layout> + ) +} + +export default Profile +``` + +You can view this [example in action](https://next-with-iron-session.vercel.app/). Check out the [`with-iron-session`](https://github.com/vercel/next.js/tree/canary/examples/with-iron-session) example to see how it works. + +### Authenticating Server-Rendered Pages + +If you export an `async` function called [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) from a page, Next.js will pre-render this page on each request using the data returned by `getServerSideProps`. + +```jsx +export async function getServerSideProps(context) { + return { + props: {}, // Will be passed to the page component as props + } +} +``` + +Let's transform the profile example to use [server-side rendering](/docs/basic-features/pages#server-side-rendering). If there's a session, return `user` as a prop to the `Profile` component in the page. Notice there is not a loading skeleton in [this example](https://next-with-iron-session.vercel.app/). + +```jsx +// pages/profile.js + +import withSession from '../lib/session' +import Layout from '../components/Layout' + +export const getServerSideProps = withSession(async function ({ req, res }) { + // Get the user's session based on the request + const user = req.session.get('user') + + if (!user) { + return { + redirect: { + destination: '/login', + permanent: false, + }, + } + } + + return { + props: { user }, + } +}) + +const Profile = ({ user }) => { + // Show the user. No loading state is required + return ( + <Layout> + <h1>Your Profile</h1> + <pre>{JSON.stringify(user, null, 2)}</pre> + </Layout> + ) +} + +export default Profile +``` + +An advantage of this pattern is preventing a flash of unauthenticated content before redirecting. It's important to note fetching user data in `getServerSideProps` will block rendering until the request to your authentication provider resolves. To prevent creating a bottleneck and increasing your TTFB ([Time to First Byte](https://web.dev/time-to-first-byte/)), you should ensure your authentication lookup is fast. Otherwise, consider [static generation](#authenticating-statically-generated-pages). + +## Authentication Providers + +Now that we've discussed authentication patterns, let's look at specific providers and explore how they're used with Next.js. + +### Bring Your Own Database + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-iron-session">with-iron-session</a></li> + <li><a href="https://github.com/nextauthjs/next-auth-example">next-auth-example</a></li> + </ul> +</details> + +If you have an existing database with user data, you'll likely want to utilize an open-source solution that's provider agnostic. + +- If you want a low-level, encrypted, and stateless session utility use [`next-iron-session`](https://github.com/vercel/next.js/tree/canary/examples/with-iron-session). +- If you want a full-featured authentication system with built-in providers (Google, Facebook, GitHub…), JWT, JWE, email/password, magic links and more… use [`next-auth`](https://github.com/nextauthjs/next-auth-example). + +Both of these libraries support either authentication pattern. If you're interested in [Passport](http://www.passportjs.org/), we also have examples for it using secure and encrypted cookies: + +- [with-passport](https://github.com/vercel/next.js/tree/canary/examples/with-passport) +- [with-passport-and-next-connect](https://github.com/vercel/next.js/tree/canary/examples/with-passport-and-next-connect) + +### Other Providers + +To see examples with other authentication providers, check out the [examples folder](https://github.com/vercel/next.js/tree/canary/examples). + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-firebase-authentication">with-firebase-authentication</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-magic">with-magic</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/auth0">auth0</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-supabase-auth-realtime-db">with-supabase-auth-realtime-db</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-userbase">with-userbase</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-supertokens">with-supertokens</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-nhost-auth-realtime-graphql">with-nhost-auth-realtime-graphql</a></li> + </ul> +</details> + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/basic-features/pages.md"> + <b>Pages:</b> + <small>Learn more about pages and the different pre-rendering methods in Next.js.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/data-fetching.md"> + <b>Data Fetching:</b> + <small>Learn more about data fetching in Next.js.</small> + </a> +</div> diff --git a/docs/basic-features/built-in-css-support.md b/docs/basic-features/built-in-css-support.md index 4b2598ba26d4d..b160a2340484c 100644 --- a/docs/basic-features/built-in-css-support.md +++ b/docs/basic-features/built-in-css-support.md @@ -4,6 +4,14 @@ description: Next.js supports including CSS files as Global CSS or CSS Modules, # Built-In CSS Support +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/basic-css">Basic CSS Example</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss">With Tailwind CSS</a></li> + </ul> +</details> + Next.js allows you to import CSS files from a JavaScript file. This is possible because Next.js extends the concept of [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) beyond JavaScript. @@ -23,7 +31,7 @@ body { } ``` -Create a [`pages/_app.js` file](/docs/advanced-features/custom-app) if not already present. +Create a [`pages/_app.js` file](/docs/advanced-features/custom-app.md) if not already present. Then, [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) the `styles.css` file. ```jsx @@ -36,12 +44,57 @@ export default function MyApp({ Component, pageProps }) { ``` These styles (`styles.css`) will apply to all pages and components in your application. -Due to the global nature of stylesheets, and to avoid conflicts, you may **only import them inside [`pages/_app.js`](/docs/advanced-features/custom-app)**. +Due to the global nature of stylesheets, and to avoid conflicts, you may **only import them inside [`pages/_app.js`](/docs/advanced-features/custom-app.md)**. In development, expressing stylesheets this way allows your styles to be hot reloaded as you edit them—meaning you can keep application state. In production, all CSS files will be automatically concatenated into a single minified `.css` file. +### Import styles from `node_modules` + +Since Next.js **9.5.4**, importing a CSS file from `node_modules` is permitted anywhere in your application. + +For global stylesheets, like `bootstrap` or `nprogress`, you should import the file inside `pages/_app.js`. +For example: + +```jsx +// pages/_app.js +import 'bootstrap/dist/css/bootstrap.css' + +export default function MyApp({ Component, pageProps }) { + return <Component {...pageProps} /> +} +``` + +For importing CSS required by a third party component, you can do so in your component. For example: + +```tsx +// components/ExampleDialog.js +import { useState } from 'react' +import { Dialog } from '@reach/dialog' +import VisuallyHidden from '@reach/visually-hidden' +import '@reach/dialog/styles.css' + +function ExampleDialog(props) { + const [showDialog, setShowDialog] = useState(false) + const open = () => setShowDialog(true) + const close = () => setShowDialog(false) + + return ( + <div> + <button onClick={open}>Open Dialog</button> + <Dialog isOpen={showDialog} onDismiss={close}> + <button className="close-button" onClick={close}> + <VisuallyHidden>Close</VisuallyHidden> + <span aria-hidden>×</span> + </button> + <p>Hello there. I am a dialog</p> + </Dialog> + </div> + ) +} +``` + ## Adding Component-Level CSS Next.js supports [CSS Modules](https://github.com/css-modules/css-modules) using the `[name].module.css` file naming convention. @@ -105,6 +158,13 @@ npm install sass Sass support has the same benefits and restrictions as the built-in CSS support detailed above. +> **Note**: Sass supports [two different syntaxes](https://sass-lang.com/documentation/syntax), each with their own extension. +> The `.scss` extension requires you use the [SCSS syntax](https://sass-lang.com/documentation/syntax#scss), +> while the `.sass` extension requires you use the [Indented Syntax ("Sass")](https://sass-lang.com/documentation/syntax#the-indented-syntax). +> +> If you're not sure which to choose, start with the `.scss` extension which is a superset of CSS, and doesn't require you learn the +> Indented Syntax ("Sass"). + ### Customizing Sass Options If you want to configure the Sass compiler you can do so by using `sassOptions` in `next.config.js`. @@ -121,26 +181,16 @@ module.exports = { } ``` -## Less and Stylus Support - -To support importing `.less` or `.styl` files you can use the following plugins: - -- [@zeit/next-less](https://github.com/zeit/next-plugins/tree/master/packages/next-less) -- [@zeit/next-stylus](https://github.com/zeit/next-plugins/tree/master/packages/next-stylus) - -If using the less plugin, don't forget to add a dependency on less as well, otherwise you'll see an error like: - -```bash -Error: Cannot find module 'less' -``` - ## CSS-in-JS <details> <summary><b>Examples</b></summary> <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/basic-css">Styled JSX</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-styled-jsx">Styled JSX</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-styled-components">Styled Components</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-emotion">Emotion</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-linaria">Linaria</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss-emotion">Tailwind CSS + Emotion</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-styletron">Styletron</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-glamor">Glamor</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-cxs">Cxs</a></li> @@ -160,7 +210,7 @@ function HiThere() { export default HiThere ``` -We bundle [styled-jsx](https://github.com/zeit/styled-jsx) to provide support for isolated scoped CSS. +We bundle [styled-jsx](https://github.com/vercel/styled-jsx) to provide support for isolated scoped CSS. The aim is to support "shadow CSS" similar to Web Components, which unfortunately [do not support server-rendering and are JS-only](https://github.com/w3c/webcomponents/issues/71). See the above examples for other popular CSS-in-JS solutions (like Styled Components). @@ -198,7 +248,13 @@ function HelloWorld() { export default HelloWorld ``` -Please see the [styled-jsx documentation](https://github.com/zeit/styled-jsx) for more examples. +Please see the [styled-jsx documentation](https://github.com/vercel/styled-jsx) for more examples. + +## FAQ + +### Does it work with JavaScript disabled? + +Yes, if you disable JavaScript the CSS will still be loaded in the production build (`next start`). During development, we require JavaScript to be enabled to provide the best developer experience with [Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh). ## Related diff --git a/docs/basic-features/data-fetching.md b/docs/basic-features/data-fetching.md index 80fa183beed2d..2eb8dd943e82e 100644 --- a/docs/basic-features/data-fetching.md +++ b/docs/basic-features/data-fetching.md @@ -2,35 +2,55 @@ description: 'Next.js has 2 pre-rendering modes: Static Generation and Server-side rendering. Learn how they work here.' --- -# Data fetching +# Data Fetching > This document is for Next.js versions 9.3 and up. If you’re using older versions of Next.js, refer to our [previous documentation](https://nextjs.org/docs/tag/v9.2.2/basic-features/data-fetching). <details open> <summary><b>Examples</b></summary> <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter">Blog Starter using markdown files</a> (<a href="https://next-blog-starter.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.now.sh/">Demo</a>)</li> - <li><a href="https://static-tweet.now.sh/">Static Tweet Demo</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-wordpress">WordPress Example</a> (<a href="https://next-blog-wordpress.vercel.app">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter">Blog Starter using markdown files</a> (<a href="https://next-blog-starter.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-strapi">Strapi Example</a> (<a href="https://next-blog-strapi.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prepr">Prepr Example</a> (<a href="https://next-blog-prepr.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-agilitycms">Agility CMS Example</a> (<a href="https://next-blog-agilitycms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic">Cosmic Example</a> (<a href="https://next-blog-cosmic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-buttercms">ButterCMS Example</a> (<a href="https://next-blog-buttercms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app/">Demo</a>)</li> + <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> </ul> </details> -In the [Pages documentation](/docs/basic-features/pages.md), we’ve explained that Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. In this page, we’ll talk in depths about data fetching strategies for each case. We recommend you to [read through the Pages documentation](/docs/basic-features/pages.md) first if you haven’t done so. +In the [Pages documentation](/docs/basic-features/pages.md), we’ve explained that Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. In this page, we’ll talk in depth about data fetching strategies for each case. We recommend you to [read through the Pages documentation](/docs/basic-features/pages.md) first if you haven’t done so. We’ll talk about the three unique Next.js functions you can use to fetch data for pre-rendering: - [`getStaticProps`](#getstaticprops-static-generation) (Static Generation): Fetch data at **build time**. -- [`getStaticPaths`](#getstaticpaths-static-generation) (Static Generation): Specify [dynamic routes](/docs/routing/dynamic-routes.md) to pre-render based on data. +- [`getStaticPaths`](#getstaticpaths-static-generation) (Static Generation): Specify [dynamic routes](/docs/routing/dynamic-routes.md) to pre-render pages based on data. - [`getServerSideProps`](#getserversideprops-server-side-rendering) (Server-side Rendering): Fetch data on **each request**. -In addition, we’ll talk briefly about how to do fetch data on the client side. +In addition, we’ll talk briefly about how to fetch data on the client side. ## `getStaticProps` (Static Generation) +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ----------------------------------------------------------------------------------------------------------------- | +| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | +| `v9.5.0` | Stable [Incremental Static Regeneration](https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration) | +| `v9.3.0` | `getStaticProps` introduced. | + +</details> + If you export an `async` function called `getStaticProps` from a page, Next.js will pre-render this page at build time using the props returned by `getStaticProps`. ```jsx @@ -44,13 +64,77 @@ export async function getStaticProps(context) { The `context` parameter is an object containing the following keys: - `params` contains the route parameters for pages using dynamic routes. For example, if the page name is `[id].js` , then `params` will look like `{ id: ... }`. To learn more, take a look at the [Dynamic Routing documentation](/docs/routing/dynamic-routes.md). You should use this together with `getStaticPaths`, which we’ll explain later. -- `preview` is `true` if the page is in the preview mode and `false` otherwise. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). +- `preview` is `true` if the page is in the preview mode and `undefined` otherwise. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). - `previewData` contains the preview data set by `setPreviewData`. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). +- `locale` contains the active locale (if enabled). +- `locales` contains all supported locales (if enabled). +- `defaultLocale` contains the configured default locale (if enabled). + +`getStaticProps` should return an object with: + +- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) +- `revalidate` - An **optional** amount in seconds after which a page re-generation can occur. Defaults to `false`. When `revalidate` is `false` it means that there is no revalidation, so the page will be cached as built until your next build. More on [Incremental Static Regeneration](#incremental-static-regeneration) +- `notFound` - An **optional** boolean value to allow the page to return a 404 status and page. Below is an example of how it works: + + ```js + export async function getStaticProps(context) { + const res = await fetch(`https://.../data`) + const data = await res.json() + + if (!data) { + return { + notFound: true, + } + } + + return { + props: { data }, // will be passed to the page component as props + } + } + ``` + + > **Note**: `notFound` is not needed for [`fallback: false`](#fallback-false) mode as only paths returned from `getStaticPaths` will be pre-rendered. + + > **Note**: With `notFound: true` the page will return a 404 even if there was a successfully generated page before. This is meant to support use-cases like user generated content getting removed by its author. + +- `redirect` - An **optional** redirect value to allow redirecting to internal and external resources. It should match the shape of `{ destination: string, permanent: boolean }`. In some rare cases, you might need to assign a custom status code for older HTTP Clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both. Below is an example of how it works: + + ```js + export async function getStaticProps(context) { + const res = await fetch(`https://...`) + const data = await res.json() + + if (!data) { + return { + redirect: { + destination: '/', + permanent: false, + }, + } + } + + return { + props: { data }, // will be passed to the page component as props + } + } + ``` + + > **Note**: Redirecting at build-time is currently not allowed and if the redirects are known at build-time they should be added in [`next.config.js`](/docs/api-reference/next.config.js/redirects.md). > **Note**: You can import modules in top-level scope for use in `getStaticProps`. -> Imports used in `getStaticProps` will not be bundled for the client-side, as [explained below](#write-server-side-code-directly). +> Imports used in `getStaticProps` will [not be bundled for the client-side](#write-server-side-code-directly). +> +> This means you can write **server-side code directly in `getStaticProps`**. +> This includes reading from the filesystem or a database. -### Simple Example +> **Note**: You should not use [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to +> call an API route in `getStaticProps`. +> Instead, directly import the logic used inside your API route. +> You may need to slightly refactor your code for this approach. +> +> Fetching from an external API is fine! + +### Example Here’s an example which uses `getStaticProps` to fetch a list of blog posts from a CMS (content management system). This example is also in the [Pages documentation](/docs/basic-features/pages.md). @@ -75,7 +159,7 @@ export async function getStaticProps() { const res = await fetch('https://.../posts') const posts = await res.json() - // By returning { props: posts }, the Blog component + // By returning { props: { posts } }, the Blog component // will receive `posts` as a prop at build time return { props: { @@ -92,7 +176,7 @@ export default Blog You should use `getStaticProps` if: - The data required to render the page is available at build time ahead of a user’s request. -- The data comes from headless CMS. +- The data comes from a headless CMS. - The data can be publicly cached (not user-specific). - The page must be pre-rendered (for SEO) and be very fast — `getStaticProps` generates HTML and JSON files, both of which can be cached by a CDN for performance. @@ -136,6 +220,91 @@ function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) { export default Blog ``` +### Incremental Static Regeneration + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://nextjs.org/commerce">Next.js Commerce</a></li> + <li><a href="https://reactions-demo.vercel.app/">GitHub Reactions Demo</a></li> + <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> + </ul> +</details> + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| -------- | ---------------- | +| `v9.5.0` | Base Path added. | + +</details> + +Next.js allows you to create or update static pages _after_ you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, **without needing to rebuild the entire site**. With ISR, you can retain the benefits of static while scaling to millions of pages. + +Consider our previous [`getStaticProps` example](#simple-example), but now with Incremental Static Regeneration enabled through the `revalidate` property: + +```jsx +function Blog({ posts }) { + return ( + <ul> + {posts.map((post) => ( + <li>{post.title}</li> + ))} + </ul> + ) +} + +// This function gets called at build time on server-side. +// It may be called again, on a serverless function, if +// revalidation is enabled and a new request comes in +export async function getStaticProps() { + const res = await fetch('https://.../posts') + const posts = await res.json() + + return { + props: { + posts, + }, + // Next.js will attempt to re-generate the page: + // - When a request comes in + // - At most once every 10 seconds + revalidate: 10, // In seconds + } +} + +// This function gets called at build time on server-side. +// It may be called again, on a serverless function, if +// the path has not been generated. +export async function getStaticPaths() { + const res = await fetch('https://.../posts') + const posts = await res.json() + + // Get the paths we want to pre-render based on posts + const paths = posts.map((post) => ({ + params: { id: post.id }, + })) + + // We'll pre-render only these paths at build time. + // { fallback: blocking } will server-render pages + // on-demand if the path doesn't exist. + return { paths, fallback: 'blocking' } +} + +export default Blog +``` + +When a request is made to a page that was pre-rendered at build time, it will initially show the cached page. + +- Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous. +- After the 10-second window, the next request will still show the cached (stale) page +- Next.js triggers a regeneration of the page in the background. +- Once the page has been successfully generated, Next.js will invalidate the cache and show the updated product page. If the background regeneration fails, the old page will stay unaltered. + +When a request is made to a path that hasn’t been generated, Next.js will server-render the page on the first request. Future requests will serve the static file from the cache. + +To learn how to persist the cache globally and handle rollbacks, learn more about [Incremental Static Regeneration](https://vercel.com/docs/next.js/incremental-static-regeneration). + ### Reading files: Use `process.cwd()` Files can be read directly from the filesystem in `getStaticProps`. @@ -147,7 +316,7 @@ Since Next.js compiles your code into a separate directory you can't use `__dirn Instead you can use `process.cwd()` which gives you the directory where Next.js is being executed. ```jsx -import fs from 'fs' +import { promises as fs } from 'fs' import path from 'path' // posts will be populated at build time by getStaticProps() @@ -169,11 +338,11 @@ function Blog({ posts }) { // direct database queries. See the "Technical details" section. export async function getStaticProps() { const postsDirectory = path.join(process.cwd(), 'posts') - const filenames = fs.readdirSync(postsDirectory) + const filenames = await fs.readdir(postsDirectory) - const posts = filenames.map((filename) => { + const posts = filenames.map(async (filename) => { const filePath = path.join(postsDirectory, filename) - const fileContents = fs.readFileSync(filePath, 'utf8') + const fileContents = await fs.readFile(filePath, 'utf8') // Generally you would parse/transform the contents // For example you can transform markdown to HTML here @@ -183,11 +352,11 @@ export async function getStaticProps() { content: fileContents, } }) - // By returning { props: posts }, the Blog component + // By returning { props: { posts } }, the Blog component // will receive `posts` as a prop at build time return { props: { - posts, + posts: await Promise.all(posts), }, } } @@ -205,7 +374,7 @@ Because `getStaticProps` runs at build time, it does **not** receive data that Note that `getStaticProps` runs only on the server-side. It will never be run on the client-side. It won’t even be included in the JS bundle for the browser. That means you can write code such as direct database queries without them being sent to browsers. You should not fetch an **API route** from `getStaticProps` — instead, you can write the server-side code directly in `getStaticProps`. -You can use [this tool](https://next-code-elimination.now.sh/) to verify what Next.js eliminates from the client-side bundle. +You can use [this tool](https://next-code-elimination.vercel.app/) to verify what Next.js eliminates from the client-side bundle. #### Statically Generates both HTML and JSON @@ -213,6 +382,8 @@ When a page with `getStaticProps` is pre-rendered at build time, in addition to This JSON file will be used in client-side routing through `next/link` ([documentation](/docs/api-reference/next/link.md)) or `next/router` ([documentation](/docs/api-reference/next/router.md)). When you navigate to a page that’s pre-rendered using `getStaticProps`, Next.js fetches this JSON file (pre-computed at build time) and uses it as the props for the page component. This means that client-side page transitions will **not** call `getStaticProps` as only the exported JSON is used. +When using Incremental Static Generation `getStaticProps` will be executed out of band to generate the JSON needed for client-side navigation. You may see this in the form of multiple requests being made for the same page, however, this is intended and has no impact on end-user performance. + #### Only allowed in a page `getStaticProps` can only be exported from a **page**. You can’t export it from non-page files. @@ -233,6 +404,16 @@ This use case is supported by Next.js by the feature called **Preview Mode**. Le ## `getStaticPaths` (Static Generation) +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| -------- | ----------------------------------------------------------------------------------------------------------------- | +| `v9.5.0` | Stable [Incremental Static Regeneration](https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration) | +| `v9.3.0` | `getStaticPaths` introduced. | + +</details> + If a page has dynamic routes ([documentation](/docs/routing/dynamic-routes.md)) and uses `getStaticProps` it needs to define a list of paths that have to be rendered to HTML at build time. If you export an `async` function called `getStaticPaths` from a page that uses dynamic routes, Next.js will statically pre-render all the paths specified by `getStaticPaths`. @@ -243,7 +424,7 @@ export async function getStaticPaths() { paths: [ { params: { ... } } // See the "paths" section below ], - fallback: true or false // See the "fallback" section below + fallback: true, false, or 'blocking' // See the "fallback" section below }; } ``` @@ -268,6 +449,7 @@ Note that the value for each `params` must match the parameters used in the page - If the page name is `pages/posts/[postId]/[commentId]`, then `params` should contain `postId` and `commentId`. - If the page name uses catch-all routes, for example `pages/[...slug]`, then `params` should contain `slug` which is an array. For example, if this array is `['foo', 'bar']`, then Next.js will statically generate the page at `/foo/bar`. +- If the page uses an optional catch-all route, supply `null`, `[]`, `undefined` or `false` to render the root-most route. For example, if you supply `slug: false` for `pages/[[...slug]]`, Next.js will statically generate the page `/`. #### The `fallback` key (required) @@ -318,13 +500,20 @@ export default Post #### `fallback: true` +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://static-tweet.vercel.app">Static generation of a large number of pages</a></li> + </ul> +</details> + If `fallback` is `true`, then the behavior of `getStaticProps` changes: -- The paths returned from `getStaticPaths` will be rendered to HTML at build time. +- The paths returned from `getStaticPaths` will be rendered to HTML at build time by `getStaticProps`. - The paths that have not been generated at build time will **not** result in a 404 page. Instead, Next.js will serve a “fallback” version of the page on the first request to such a path (see [“Fallback pages”](#fallback-pages) below for details). - In the background, Next.js will statically generate the requested path HTML and JSON. This includes running `getStaticProps`. - When that’s done, the browser receives the JSON for the generated path. This will be used to automatically render the page with the required props. From the user’s perspective, the page will be swapped from the fallback page to the full page. -- At the same time, Next.js adds this path to the list of pre-rendered pages. Subsequent requests to the same path will serve the generated page, just like other pages pre-rendered at build time. +- At the same time, Next.js adds this path to the list of pre-rendered pages. Subsequent requests to the same path will serve the generated page, like other pages pre-rendered at build time. > `fallback: true` is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). @@ -372,7 +561,12 @@ export async function getStaticProps({ params }) { const post = await res.json() // Pass post data to the page via props - return { props: { post } } + return { + props: { post }, + // Re-generate the post at most once per second + // if a request comes in + revalidate: 1, + } } export default Post @@ -386,6 +580,23 @@ Instead, you may statically generate a small subset of pages and use `fallback: This ensures that users always have a fast experience while preserving fast builds and the benefits of Static Generation. +`fallback: true` will not _update_ generated pages, for that take a look at [Incremental Static Regeneration](#incremental-static-regeneration). + +#### `fallback: 'blocking'` + +If `fallback` is `'blocking'`, new paths not returned by `getStaticPaths` will wait for the HTML to be generated, identical to SSR (hence why _blocking_), and then be cached for future requests so it only happens once per path. + +`getStaticProps` will behave as follows: + +- The paths returned from `getStaticPaths` will be rendered to HTML at build time by `getStaticProps`. +- The paths that have not been generated at build time will **not** result in a 404 page. Instead, Next.js will SSR on the first request and return the generated HTML. +- When that’s done, the browser receives the HTML for the generated path. From the user’s perspective, it will transition from "the browser is requesting the page" to "the full page is loaded". There is no flash of loading/fallback state. +- At the same time, Next.js adds this path to the list of pre-rendered pages. Subsequent requests to the same path will serve the generated page, like other pages pre-rendered at build time. + +`fallback: 'blocking'` will not _update_ generated pages by default. To update generated pages, use [Incremental Static Regeneration](#incremental-static-regeneration) in conjunction with `fallback: 'blocking'`. + +> `fallback: 'blocking'` is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). + ### When should I use `getStaticPaths`? You should use `getStaticPaths` if you’re statically pre-rendering pages that use dynamic routes. @@ -426,6 +637,16 @@ In development (`next dev`), `getStaticPaths` will be called on every request. ## `getServerSideProps` (Server-side Rendering) +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ------------------------------------------------------------------- | +| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | +| `v9.3.0` | `getServerSideProps` introduced. | + +</details> + If you export an `async` function called `getServerSideProps` from a page, Next.js will pre-render this page on each request using the data returned by `getServerSideProps`. ```js @@ -441,14 +662,72 @@ The `context` parameter is an object containing the following keys: - `params`: If this page uses a dynamic route, `params` contains the route parameters. If the page name is `[id].js` , then `params` will look like `{ id: ... }`. To learn more, take a look at the [Dynamic Routing documentation](/docs/routing/dynamic-routes.md). - `req`: [The HTTP IncomingMessage object](https://nodejs.org/api/http.html#http_class_http_incomingmessage). - `res`: [The HTTP response object](https://nodejs.org/api/http.html#http_class_http_serverresponse). -- `query`: The query string. +- `query`: An object representing the query string. - `preview`: `preview` is `true` if the page is in the preview mode and `false` otherwise. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). - `previewData`: The preview data set by `setPreviewData`. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). +- `resolvedUrl`: A normalized version of the request URL that strips the `_next/data` prefix for client transitions and includes original query values. +- `locale` contains the active locale (if enabled). +- `locales` contains all supported locales (if enabled). +- `defaultLocale` contains the configured default locale (if enabled). + +`getServerSideProps` should return an object with: + +- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) or a Promise that resolves to a serializable object. +- `notFound` - An **optional** boolean value to allow the page to return a 404 status and page. Below is an example of how it works: + + ```js + export async function getServerSideProps(context) { + const res = await fetch(`https://...`) + const data = await res.json() + + if (!data) { + return { + notFound: true, + } + } + + return { + props: {}, // will be passed to the page component as props + } + } + ``` + +- `redirect` - An **optional** redirect value to allow redirecting to internal and external resources. It should match the shape of `{ destination: string, permanent: boolean }`. In some rare cases, you might need to assign a custom status code for older HTTP Clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both. Below is an example of how it works: + + ```js + export async function getServerSideProps(context) { + const res = await fetch(`https://.../data`) + const data = await res.json() + + if (!data) { + return { + redirect: { + destination: '/', + permanent: false, + }, + } + } + + return { + props: {}, // will be passed to the page component as props + } + } + ``` > **Note**: You can import modules in top-level scope for use in `getServerSideProps`. -> Imports used in `getServerSideProps` will not be bundled for the client-side, as [explained below](#only-runs-on-server-side). +> Imports used in `getServerSideProps` will not be bundled for the client-side. +> +> This means you can write **server-side code directly in `getServerSideProps`**. +> This includes reading from the filesystem or a database. + +> **Note**: You should not use [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to +> call an API route in `getServerSideProps`. +> Instead, directly import the logic used inside your API route. +> You may need to slightly refactor your code for this approach. +> +> Fetching from an external API is fine! -### Simple example +### Example Here’s an example which uses `getServerSideProps` to fetch data at request time and pre-renders it. This example is also in the [Pages documentation](/docs/basic-features/pages.md). @@ -517,12 +796,12 @@ export default Page #### Only runs on server-side -`getServerSideProps` only runs on server-side and never runs on the browser. If a page uses `getServerSideProps` , then: +`getServerSideProps` only runs on server-side and never runs on the browser. If a page uses `getServerSideProps`, then: - When you request this page directly, `getServerSideProps` runs at the request time, and this page will be pre-rendered with the returned props. - When you request this page on client-side page transitions through `next/link` ([documentation](/docs/api-reference/next/link.md)) or `next/router` ([documentation](/docs/api-reference/next/router.md)), Next.js sends an API request to the server, which runs `getServerSideProps`. It’ll return JSON that contains the result of running `getServerSideProps`, and the JSON will be used to render the page. All this work will be handled automatically by Next.js, so you don’t need to do anything extra as long as you have `getServerSideProps` defined. -You can use [this tool](https://next-code-elimination.now.sh/) to verify what Next.js eliminates from the client-side bundle. +You can use [this tool](https://next-code-elimination.vercel.app/) to verify what Next.js eliminates from the client-side bundle. #### Only allowed in a page @@ -541,13 +820,15 @@ This approach works well for user dashboard pages, for example. Because a dashbo ### SWR -The team behind Next.js has created a React hook for data fetching called [**SWR**](https://swr.now.sh/). We highly recommend it if you’re fetching data on the client side. It handles caching, revalidation, focus tracking, refetching on interval, and more. And you can use it like so: +The team behind Next.js has created a React hook for data fetching called [**SWR**](https://swr.vercel.app/). We highly recommend it if you’re fetching data on the client side. It handles caching, revalidation, focus tracking, refetching on interval, and more. And you can use it like so: ```jsx import useSWR from 'swr' +const fetcher = (url) => fetch(url).then((res) => res.json()) + function Profile() { - const { data, error } = useSWR('/api/user', fetch) + const { data, error } = useSWR('/api/user', fetcher) if (error) return <div>failed to load</div> if (!data) return <div>loading...</div> @@ -555,7 +836,7 @@ function Profile() { } ``` -[Check out the SWR documentation to learn more](https://swr.now.sh/). +[Check out the SWR documentation to learn more](https://swr.vercel.app/). ## Learn more diff --git a/docs/basic-features/environment-variables.md b/docs/basic-features/environment-variables.md index 3d0fb547085d0..7e2ff8e70c3bf 100644 --- a/docs/basic-features/environment-variables.md +++ b/docs/basic-features/environment-variables.md @@ -4,104 +4,146 @@ description: Learn to add and access environment variables in your Next.js appli # Environment Variables -> This document is for Next.js versions 9.4 and up. If you’re using an older version of Next.js, refer to [Environment Variables in next.config.js](/docs/api-reference/next.config.js/environment-variables.md). +> This document is for Next.js versions 9.4 and up. If you’re using an older version of Next.js, upgrade or refer to [Environment Variables in next.config.js](/docs/api-reference/next.config.js/environment-variables.md). + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/environment-variables">Environment Variables</a></li> + </ul> +</details> Next.js comes with built-in support for environment variables, which allows you to do the following: -- [Inline variables starting with `NEXT_PUBLIC_`](#inlined-environment-variables) -- [Use `.env` to add custom environment variables](#exposing-environment-variables) +- [Use `.env.local` to load environment variables](#loading-environment-variables) +- [Expose environment variables to the browser by prefixing with `NEXT_PUBLIC_`](#exposing-environment-variables-to-the-browser) -## Inlined Environment Variables +## Loading Environment Variables -Next.js will inline any environment variable that starts with `NEXT_PUBLIC_` in your application. Inlining means replacing the variable with the value. For example, the following page: +Next.js has built-in support for loading environment variables from `.env.local` into `process.env`. -```jsx -export default function Page() { - return <h1>The public value is: {process.env.NEXT_PUBLIC_EXAMPLE_KEY}</h1> -} +An example `.env.local`: + +```bash +DB_HOST=localhost +DB_USER=myuser +DB_PASS=mypassword ``` -Will end up being: +This loads `process.env.DB_HOST`, `process.env.DB_USER`, and `process.env.DB_PASS` into the Node.js environment automatically allowing you to use them in [Next.js data fetching methods](/docs/basic-features/data-fetching.md) and [API routes](/docs/api-routes/introduction.md). + +For example, using [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation): -```jsx -export default function Page() { - return <h1>The public value is: {'my-value'}</h1> +```js +// pages/index.js +export async function getStaticProps() { + const db = await myDB.connect({ + host: process.env.DB_HOST, + username: process.env.DB_USER, + password: process.env.DB_PASS, + }) + // ... } ``` -Next.js replaced `process.env.NEXT_PUBLIC_EXAMPLE_KEY` with its value, that in this case is `'my-value'`. - -You can use the shell or any other tool that runs before the [Next.js CLI](/api-reference/cli) to add environment variables. For example, using bash: +> **Note**: In order to keep server-only secrets safe, Next.js replaces `process.env.*` with the correct values +> at build time. This means that `process.env` is not a standard JavaScript object, so you’re not able to +> use [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment). +> Environment variables must be referenced as e.g. `process.env.NEXT_PUBLIC_PUBLISHABLE_KEY`, _not_ `const { NEXT_PUBLIC_PUBLISHABLE_KEY } = process.env`. + +> **Note**: Next.js will automatically expand variables (`$VAR`) inside of your `.env*` files. +> This allows you to reference other secrets, like so: +> +> ```bash +> # .env +> HOSTNAME=localhost +> PORT=8080 +> HOST=http://$HOSTNAME:$PORT +> ``` +> +> If you are trying to use a variable with a `$` in the actual value, it needs to be escaped like so: `\$`. +> +> For example: +> +> ```bash +> # .env +> A=abc +> +> # becomes "preabc" +> WRONG=pre$A +> +> # becomes "pre$A" +> CORRECT=pre\$A +> ``` + +## Exposing Environment Variables to the Browser + +By default all environment variables loaded through `.env.local` are only available in the Node.js environment, meaning they won't be exposed to the browser. + +In order to expose a variable to the browser you have to prefix the variable with `NEXT_PUBLIC_`. For example: ```bash -NEXT_PUBLIC_EXAMPLE_KEY=my-value npx next dev +NEXT_PUBLIC_ANALYTICS_ID=abcdefghijk ``` -Or using [cross-env](https://github.com/kentcdodds/cross-env) for Windows and Unix support: +This loads `process.env.NEXT_PUBLIC_ANALYTICS_ID` into the Node.js environment automatically, allowing you to use it anywhere in your code. The value will be inlined into JavaScript sent to the browser because of the `NEXT_PUBLIC_` prefix. This inlining occurs at build time, so your various `NEXT_PUBLIC_` envs need to be set when the project is built. -```bash -npx cross-env NEXT_PUBLIC_EXAMPLE_KEY=my-value next dev -``` +```js +// pages/index.js +import setupAnalyticsService from '../lib/my-analytics-service' -### Caveats +// NEXT_PUBLIC_ANALYTICS_ID can be used here as it's prefixed by NEXT_PUBLIC_ +setupAnalyticsService(process.env.NEXT_PUBLIC_ANALYTICS_ID) -- Trying to destructure `process.env` variables won't work due to the limitations of webpack's [DefinePlugin](https://webpack.js.org/plugins/define-plugin/). -- To avoid exposing secrets, do not use the `NEXT_PUBLIC_` prefix for them. Instead, [expose the variables using `.env`](#exposing-environment-variables). +function HomePage() { + return <h1>Hello World</h1> +} -## Exposing Environment Variables +export default HomePage +``` -Next.js allows you to expose variables using an environment variables file (`.env`), with included support for multiple environments. It works like this: +## Default Environment Variables -- `.env` - Contains environment variables for all environments -- `.env.local` - Local variable overrides for all environments -- `.env.[environment]` - Environment variables for one environment. For example: `.env.development` -- `.env.[environment].local` - Local variable overrides for one environment. For example: `.env.development.local` +In general only one `.env.local` file is needed. However, sometimes you might want to add some defaults for the `development` (`next dev`) or `production` (`next start`) environment. -> **Note**: `.env` files **should be** included in your repository, and **`.env*.local` should be in `.gitignore`**, as those files are intended to be ignored. Consider `.local` files as a good place for secrets, and non-local files as a good place for defaults. +Next.js allows you to set defaults in `.env` (all environments), `.env.development` (development environment), and `.env.production` (production environment). -The supported environments are `development`, `production` and `test`. The environment is selected in the following way: +`.env.local` always overrides the defaults set. -- [`next dev`](/docs/api-reference/cli#development) uses `development` -- [`next build`](/docs/api-reference/cli#build) and [`next start`](/docs/api-reference/cli#production) use `production` +> **Note**: `.env`, `.env.development`, and `.env.production` files should be included in your repository as they define defaults. **`.env.*.local` should be added to `.gitignore`**, as those files are intended to be ignored. `.env.local` is where secrets can be stored. -If the same environment variable is defined multiple times, the priority of which variable to use is decided in the following order: +## Environment Variables on Vercel -- Already defined environment variables have the higher priority. For example: `MY_KEY=value next dev` -- `.env.[environment].local` -- `.env.local` -- `.env.[environment]` -- `.env` +When deploying your Next.js application to [Vercel](https://vercel.com), Environment Variables can be configured [in the Project Settings](https://vercel.com/docs/environment-variables). -For example, consider the file `.env.local` with the following content: +All types of Environment Variables should be configured there. Even Environment Variables used in Development – which can be [downloaded onto your local device](https://vercel.com/docs/environment-variables#development-environment-variables) afterwards. + +If you've configured [Development Environment Variables](https://vercel.com/docs/environment-variables#development-environment-variables) you can pull them into a `.env.local` for usage on your local machine using the following command: ```bash -API_KEY='my-secret-api-key' -NEXT_PUBLIC_APP_LOCALE='en-us' +vercel env pull .env.local ``` -And the following page: +When using the Vercel CLI to deploy make sure you add a [`.vercelignore`](https://vercel.com/guides/prevent-uploading-sourcepaths-with-vercelignore?query=vercelignore#allowlist) that includes files that should not be uploaded, generally these are the same files included in `.gitignore`. -```jsx -export default function Page() { - return <h1>The locale is set to: {process.env.NEXT_PUBLIC_APP_LOCALE}</h1> -} +## Test Environment Variables -export async function getStaticProps() { - const db = await myDB(process.env.API_KEY) - // ... -} -``` +Apart from `development` and `production` environments, there is a 3rd option available: `test`. In the same way you can set defaults for development or production environments, you can do the same with `.env.test` file for testing environment (though this one is not so common as the previous two). -`process.env.NEXT_PUBLIC_APP_LOCALE` will be replaced with `'en-us'` in the build output. This is because variables that start with `NEXT_PUBLIC_` will be [inlined at build time](#inlined-environment-variables). +This one is useful when running tests with tools like `jest` or `cypress` where you need to set specific environment vars only for testing purposes. Test default values will be loaded if `NODE_ENV` is set to `test`, though you usually don't need to do this manually as testing tools will address it for you. -`process.env.API_KEY` will be a variable with `'my-secret-api-key'` at build time and runtime, but the build output will not contain this key. This is because `process.env.API_KEY` is only used by [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) which [runs only on the server](/docs/basic-features/data-fetching.md#write-server-side-code-directly) — and only the props returned by `getStaticProps` are included in the client build. Same goes for our other [data fetching methods](/docs/basic-features/data-fetching.md). +There is a small difference between `test` environment, and both `development` and `production` that you need to bear in mind: `.env.local` won't be loaded, as you expect tests to produce the same results for everyone. This way every test execution will use same env defaults across different executions by ignoring your `.env.local` (which is intended to override the default set). -Now, if you add a `.env` file like this one: +> **Note**: similar to Default Environment Variables, `.env.test` file should be included in your repository, but `.env.test.local` shouldn't, as `.env*.local` are intended to be ignored through `.gitignore`. -```bash -API_KEY='default-api-key' -CLIENT_KEY='default-client-key' -NEXT_PUBLIC_APP_LOCALE='en-us' -``` +While running unit tests you can make sure to load your environment variables the same way Next.js does by leveraging the `loadEnvConfig` function from the `@next/env` package. + +```js +// The below can be used in a Jest global setup file or similar for your testing set-up +import { loadEnvConfig } from '@next/env' -Both `API_KEY` and `NEXT_PUBLIC_APP_LOCALE` will be ignored as `.env.local` has a higher priority, but `CLIENT_KEY` will become available. +export default async () => { + const projectDir = process.cwd() + loadEnvConfig(projectDir) +} +``` diff --git a/docs/basic-features/eslint.md b/docs/basic-features/eslint.md new file mode 100644 index 0000000000000..6d3028a6a5f17 --- /dev/null +++ b/docs/basic-features/eslint.md @@ -0,0 +1,233 @@ +--- +description: Next.js provides an integrated ESLint experience by default. These conformance rules help you use Next.js in the optimal way. +--- + +# ESLint + +Since version **11.0.0**, Next.js provides an integrated [ESLint](https://eslint.org/) experience out of the box. Add `next lint` as a script to `package.json`: + +```json +"scripts": { + "lint": "next lint" +} +``` + +Then run `npm run lint` or `yarn lint`: + +```bash +yarn lint +``` + +If you don't already have ESLint configured in your application, you will be guided through the installation and configuration process. + +```bash +yarn lint + +# You'll see a prompt like this: +# +# ? How would you like to configure ESLint? +# +# ❯ Base configuration + Core Web Vitals rule-set (recommended) +# Base configuration +# None +``` + +One of the following three options can be selected: + +- **Strict**: Includes Next.js' base ESLint configuration along with a stricter [Core Web Vitals rule-set](/docs/basic-features/eslint.md#core-web-vitals). This is the recommended configuration for developers setting up ESLint for the first time. + + ```json + { + "extends": "next/core-web-vitals" + } + ``` + +- **Base**: Includes Next.js' base ESLint configuration. + + ```json + { + "extends": "next" + } + ``` + +- **Cancel**: Does not include any ESLint configuration. Only select this option if you plan on setting up your own custom ESLint configuration. + +If either of the two configuration options are selected, Next.js will automatically install `eslint` and `eslint-config-next` as development dependencies in your application and create an `.eslintrc.json` file in the root of your project that includes your selected configuration. + +You can now run `next lint` every time you want to run ESLint to catch errors. Once ESLint has been set up, it will also automatically run during every build (`next build`). Errors will fail the build, while warnings will not. + +> If you do not want ESLint to run during `next build`, refer to the documentation for [Ignoring ESLint](/docs/api-reference/next.config.js/ignoring-eslint.md). + +We recommend using an appropriate [integration](https://eslint.org/docs/user-guide/integrations#editors) to view warnings and errors directly in your code editor during development. + +## ESLint Config + +The default configuration (`eslint-config-next`) includes everything you need to have an optimal out-of-the-box linting experience in Next.js. If you do not have ESLint already configured in your application, we recommend using `next lint` to set up ESLint along with this configuration. + +> If you would like to use `eslint-config-next` along with other ESLint configurations, refer to the [Additional Configurations](/docs/basic-features/eslint.md#additional-configurations) section to learn how to do so without causing any conflicts. + +Recommended rule-sets from the following ESLint plugins are all used within `eslint-config-next`: + +- [`eslint-plugin-react`](https://www.npmjs.com/package/eslint-plugin-react) +- [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) +- [`eslint-plugin-next`](https://www.npmjs.com/package/@next/eslint-plugin-next) + +You can see the full details of the shareable configuration in the [`eslint-config-next`](https://www.npmjs.com/package/eslint-config-next) package. + +This will take precedence over the configuration from `next.config.js`. + +## ESLint Plugin + +Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/package/@next/eslint-plugin-next), already bundled within the base configuration that makes it possible to catch common issues and problems in a Next.js application. The full set of rules is as follows: + +| | Rule | Description | +| :-: | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| ✔️ | [next/google-font-display](https://nextjs.org/docs/messages/google-font-display) | Enforce optional or swap font-display behavior with Google Fonts | +| ✔️ | [next/google-font-preconnect](https://nextjs.org/docs/messages/google-font-preconnect) | Enforce preconnect usage with Google Fonts | +| ✔️ | [next/link-passhref](https://nextjs.org/docs/messages/link-passhref) | Enforce passHref prop usage with custom Link components | +| ✔️ | [next/no-css-tags](https://nextjs.org/docs/messages/no-css-tags) | Prevent manual stylesheet tags | +| ✔️ | [next/no-document-import-in-page](https://nextjs.org/docs/messages/no-document-import-in-page) | Disallow importing next/document outside of pages/document.js | +| ✔️ | [next/no-head-import-in-document](https://nextjs.org/docs/messages/no-head-import-in-document) | Disallow importing next/head in pages/document.js | +| ✔️ | [next/no-html-link-for-pages](https://nextjs.org/docs/messages/no-html-link-for-pages) | Prohibit HTML anchor links to pages without a Link component | +| ✔️ | [next/no-img-element](https://nextjs.org/docs/messages/no-img-element) | Prohibit usage of HTML <img> element | +| ✔️ | [next/no-page-custom-font](https://nextjs.org/docs/messages/no-page-custom-font) | Prevent page-only custom fonts | +| ✔️ | [next/no-sync-scripts](https://nextjs.org/docs/messages/no-sync-scripts) | Forbid synchronous scripts | +| ✔️ | [next/no-title-in-document-head](https://nextjs.org/docs/messages/no-title-in-document-head) | Disallow using <title> with Head from next/document | +| ✔️ | [next/no-unwanted-polyfillio](https://nextjs.org/docs/messages/no-unwanted-polyfillio) | Prevent duplicate polyfills from Polyfill.io | +| ✔️ | [next/inline-script-id](https://nextjs.org/docs/messages/inline-script-id) | Enforce id attribute on next/script components with inline content | +| ✔️ | next/no-typos | Ensure no typos were made declaring [Next.js's data fetching function](https://nextjs.org/docs/basic-features/data-fetching) | +| ✔️ | [next/next-script-for-ga](https://nextjs.org/docs/messages/next-script-for-ga) | Use the Script component to defer loading of the script until necessary. | + +- ✔: Enabled in the recommended configuration + +If you already have ESLint configured in your application, we recommend extending from this plugin directly instead of including `eslint-config-next` unless a few conditions are met. Refer to the [Recommended Plugin Ruleset](/docs/basic-features/eslint.md#recommended-plugin-ruleset) to learn more. + +### Custom Settings + +#### `rootDir` + +If you're using `eslint-plugin-next` in a project where Next.js isn't installed in your root directory (such as a monorepo), you can tell `eslint-plugin-next` where to find your Next.js application using the `settings` property in your `.eslintrc`: + +```json +{ + "extends": "next", + "settings": { + "next": { + "rootDir": "/packages/my-app/" + } + } +} +``` + +`rootDir` can be a path (relative or absolute), a glob (i.e. `"/packages/*/"`), or an array of paths and/or globs. + +## Linting Custom Directories + +By default, Next.js will run ESLint for all files in the `pages/`, `components/`, and `lib/` directories. However, you can specify which directories using the `dirs` option in the `eslint` config in `next.config.js` for production builds: + +```js +module.exports = { + eslint: { + dirs: ['pages', 'utils'], // Only run ESLint on the 'pages' and 'utils' directories during production builds (next build) + }, +} +``` + +Similarly, the `--dir` flag can be used for `next lint`: + +```bash +next lint --dir pages --dir utils +``` + +## Caching + +To improve performance, information of files processed by ESLint are cached by default. This is stored in `.next/cache` or in your defined [build directory](/docs/api-reference/next.config.js/setting-a-custom-build-directory). If you include any ESLint rules that depend on more than the contents of a single source file and need to disable the cache, use the `--no-cache` flag with `next lint`. + +```bash +next lint --no-cache +``` + +## Disabling Rules + +If you would like to modify or disable any rules provided by the supported plugins (`react`, `react-hooks`, `next`), you can directly change them using the `rules` property in your `.eslintrc`: + +```json +{ + "extends": "next", + "rules": { + "react/no-unescaped-entities": "off", + "@next/next/no-page-custom-font": "off" + } +} +``` + +### Core Web Vitals + +The `next/core-web-vitals` rule set is enabled when `next lint` is run for the first time and the **strict** option is selected. + +```json +{ + "extends": "next/core-web-vitals" +} +``` + +`next/core-web-vitals` updates `eslint-plugin-next` to error on a number of rules that are warnings by default if they affect [Core Web Vitals](https://web.dev/vitals/). + +> The `next/core-web-vitals` entry point is automatically included for new applications built with [Create Next App](/docs/api-reference/create-next-app.md). + +## Usage with Prettier + +ESLint also contains code formatting rules, which can conflict with your existing [Prettier](https://prettier.io/) setup. We recommend including [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) in your ESLint config to make ESLint and Prettier work together. + +```json +{ + "extends": ["next", "prettier"] +} +``` + +## Migrating Existing Config + +### Recommended Plugin Ruleset + +If you already have ESLint configured in your application and any of the following conditions are true: + +- You have one or more of the following plugins already installed (either separately or through a different config such as `airbnb` or `react-app`): + - `react` + - `react-hooks` + - `jsx-a11y` + - `import` +- You've defined specific `parserOptions` that are different from how Babel is configured within Next.js (this is not recommended unless you have [customized your Babel configuration](/docs/advanced-features/customizing-babel-config.md)) +- You have `eslint-plugin-import` installed with Node.js and/or TypeScript [resolvers](https://github.com/benmosher/eslint-plugin-import#resolvers) defined to handle imports + +Then we recommend either removing these settings if you prefer how these properties have been configured within [`eslint-config-next`](https://github.com/vercel/next.js/blob/canary/packages/eslint-config-next/index.js) or extending directly from the Next.js ESLint plugin instead: + +```js +module.exports = { + extends: [ + //... + 'plugin:@next/next/recommended', + ], +} +``` + +The plugin can be installed normally in your project without needing to run `next lint`: + +```bash +npm install --save-dev @next/eslint-plugin-next +# or +yarn add --dev @next/eslint-plugin-next +``` + +This eliminates the risk of collisions or errors that can occur due to importing the same plugin or parser across multiple configurations. + +### Additional Configurations + +If you already use a separate ESLint configuration and want to include `eslint-config-next`, ensure that it is extended last after other configurations. For example: + +``` +{ + "extends": ["eslint:recommended", "next"] +} +``` + +The `next` configuration already handles setting default values for the `parser`, `plugins` and `settings` properties. There is no need to manually re-declare any of these properties unless you need a different configuration for your use case. If you include any other shareable configurations, **you will need to make sure that these properties are not overwritten or modified**. Otherwise, we recommend removing any configurations that share behavior with the `next` configuration or extending directly from the Next.js ESLint plugin as mentioned above. diff --git a/docs/basic-features/fast-refresh.md b/docs/basic-features/fast-refresh.md new file mode 100644 index 0000000000000..3b5678ba7e60b --- /dev/null +++ b/docs/basic-features/fast-refresh.md @@ -0,0 +1,120 @@ +--- +description: + Next.js' Fast Refresh is a new hot reloading experience that gives you + instantaneous feedback on edits made to your React components. +--- + +# Fast Refresh + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/fast-refresh-demo">Fast Refresh Demo</a></li> + </ul> +</details> + +Fast Refresh is a Next.js feature that gives you instantaneous feedback on +edits made to your React components. Fast Refresh is enabled by default in all +Next.js applications on **9.4 or newer**. With Next.js Fast Refresh enabled, +most edits should be visible within a second, **without losing component +state**. + +## How It Works + +- If you edit a file that **only exports React component(s)**, Fast Refresh will + update the code only for that file, and re-render your component. You can edit + anything in that file, including styles, rendering logic, event handlers, or + effects. +- If you edit a file with exports that _aren't_ React components, Fast Refresh + will re-run both that file, and the other files importing it. So if both + `Button.js` and `Modal.js` import `theme.js`, editing `theme.js` will update + both components. +- Finally, if you **edit a file** that's **imported by files outside of the + React tree**, Fast Refresh **will fall back to doing a full reload**. You + might have a file which renders a React component but also exports a value + that is imported by a **non-React component**. For example, maybe your + component also exports a constant, and a non-React utility file imports it. In + that case, consider migrating the constant to a separate file and importing it + into both files. This will re-enable Fast Refresh to work. Other cases can + usually be solved in a similar way. + +## Error Resilience + +### Syntax Errors + +If you make a syntax error during development, you can fix it and save the file +again. The error will disappear automatically, so you won't need to reload the +app. **You will not lose component state**. + +### Runtime Errors + +If you make a mistake that leads to a runtime error inside your component, +you'll be greeted with a contextual overlay. Fixing the error will automatically +dismiss the overlay, without reloading the app. + +Component state will be retained if the error did not occur during rendering. If +the error did occur during rendering, React will remount your application using +the updated code. + +If you have [error boundaries](https://reactjs.org/docs/error-boundaries.html) +in your app (which is a good idea for graceful failures in production), they +will retry rendering on the next edit after a rendering error. This means having +an error boundary can prevent you from always getting reset to the root app +state. However, keep in mind that error boundaries shouldn't be _too_ granular. +They are used by React in production, and should always be designed +intentionally. + +## Limitations + +Fast Refresh tries to preserve local React state in the component you're +editing, but only if it's safe to do so. Here's a few reasons why you might see +local state being reset on every edit to a file: + +- Local state is not preserved for class components (only function components + and Hooks preserve state). +- The file you're editing might have _other_ exports in addition to a React + component. +- Sometimes, a file would export the result of calling higher-order component + like `HOC(WrappedComponent)`. If the returned component is a + class, state will be reset. +- Anonymous arrow functions like `export default () => <div />;` cause Fast Refresh to not preserve local component state. For large codebases you can use our [`name-default-component` codemod](/docs/advanced-features/codemods.md#name-default-component). + +As more of your codebase moves to function components and Hooks, you can expect +state to be preserved in more cases. + +## Tips + +- Fast Refresh preserves React local state in function components (and Hooks) by + default. +- Sometimes you might want to _force_ the state to be reset, and a component to + be remounted. For example, this can be handy if you're tweaking an animation + that only happens on mount. To do this, you can add `// @refresh reset` + anywhere in the file you're editing. This directive is local to the file, and + instructs Fast Refresh to remount components defined in that file on every + edit. +- You can put `console.log` or `debugger;` into the components you edit during + development. + +## Fast Refresh and Hooks + +When possible, Fast Refresh attempts to preserve the state of your component +between edits. In particular, `useState` and `useRef` preserve their previous +values as long as you don't change their arguments or the order of the Hook +calls. + +Hooks with dependencies—such as `useEffect`, `useMemo`, and `useCallback`—will +_always_ update during Fast Refresh. Their list of dependencies will be ignored +while Fast Refresh is happening. + +For example, when you edit `useMemo(() => x * 2, [x])` to +`useMemo(() => x * 10, [x])`, it will re-run even though `x` (the dependency) +has not changed. If React didn't do that, your edit wouldn't reflect on the +screen! + +Sometimes, this can lead to unexpected results. For example, even a `useEffect` +with an empty array of dependencies would still re-run once during Fast Refresh. + +However, writing code resilient to occasional re-running of `useEffect` is a good practice even +without Fast Refresh. It will make it easier for you to introduce new dependencies to it later on +and it's enforced by [React Strict Mode](/docs/api-reference/next.config.js/react-strict-mode.md), +which we highly recommend enabling. diff --git a/docs/basic-features/font-optimization.md b/docs/basic-features/font-optimization.md new file mode 100644 index 0000000000000..7f9d06a1557b2 --- /dev/null +++ b/docs/basic-features/font-optimization.md @@ -0,0 +1,102 @@ +--- +description: Next.js supports built-in web font optimization to inline font CSS. Learn more here. +--- + +# Font Optimization + +Since version **10.2**, Next.js has built-in web font optimization. + +By default, Next.js will automatically inline font CSS at build time, eliminating an extra round trip to fetch font declarations. This results in improvements to [First Contentful Paint (FCP)](https://web.dev/fcp/) and [Largest Contentful Paint (LCP)](https://vercel.com/blog/core-web-vitals#largest-contentful-paint). For example: + +```js +// Before +<link + href="https://fonts.googleapis.com/css2?family=Inter" + rel="stylesheet" +/> + +// After +<style data-href="https://fonts.googleapis.com/css2?family=Inter"> + @font-face{font-family:'Inter';font-style:normal... +</style> +``` + +## Usage + +To add a web font to your Next.js application, override `next/head`. For example, you can add a font to a specific page: + +```js +// pages/index.js + +import Head from 'next/head' + +export default function IndexPage() { + return ( + <div> + <Head> + <link + href="https://fonts.googleapis.com/css2?family=Inter&display=optional" + rel="stylesheet" + /> + </Head> + <p>Hello world!</p> + </div> + ) +} +``` + +or to your entire application with a [Custom `Document`](/docs/advanced-features/custom-document.md). + +```js +// pages/_document.js + +import Document, { Html, Head, Main, NextScript } from 'next/document' + +class MyDocument extends Document { + render() { + return ( + <Html> + <Head> + <link + href="https://fonts.googleapis.com/css2?family=Inter&display=optional" + rel="stylesheet" + /> + </Head> + <body> + <Main /> + <NextScript /> + </body> + </Html> + ) + } +} + +export default MyDocument +``` + +Automatic Webfont Optimization currently supports Google Fonts and Typekit with support for other font providers coming soon. We're also planning to add control over [loading strategies](https://github.com/vercel/next.js/issues/21555) and `font-display` values. + +See [Google Font Display](https://nextjs.org/docs/messages/google-font-display) for more information. + +## Disabling Optimization + +If you do not want Next.js to optimize your fonts, you can opt-out. + +```js +// next.config.js + +module.exports = { + optimizeFonts: false, +} +``` + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/advanced-features/custom-document.md"> + <b>Custom Document</b> + <small>Learn how to augment your application's html and body tags.</small> + </a> +</div> diff --git a/docs/basic-features/image-optimization.md b/docs/basic-features/image-optimization.md new file mode 100644 index 0000000000000..7c8e28074cd7d --- /dev/null +++ b/docs/basic-features/image-optimization.md @@ -0,0 +1,218 @@ +--- +description: Next.js supports built-in image optimization, as well as third party loaders for Imgix, Cloudinary, and more! Learn more here. +--- + +# Image Component and Image Optimization + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/image-component">Image Component</a></li> + </ul> +</details> + +Since version **10.0.0**, Next.js has a built-in Image Component and Automatic Image Optimization. + +The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web. + +The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. + +Automatic Image Optimization works with any image source. Even if the image is hosted by an external data source, like a CMS, it can still be optimized. + +Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Unlike static site generators and static-only solutions, your build times aren't increased, whether shipping 10 images or 10 million images. + +Images are lazy loaded by default. That means your page speed isn't penalized for images outside the viewport. Images load as they are scrolled into viewport. + +Images are always rendered in such a way as to avoid [Cumulative Layout Shift](https://web.dev/cls/), a [Core Web Vital](https://web.dev/vitals/) that Google [uses in search ranking](https://developers.google.com/search/blog/2020/05/evaluating-page-experience). + +## Image Component + +To add an image to your application, import the [`next/image`](/docs/api-reference/next/image.md) component: + +```jsx +import Image from 'next/image' + +function Home() { + return ( + <> + <h1>My Homepage</h1> + <Image + src="/me.png" + alt="Picture of the author" + width={500} + height={500} + /> + <p>Welcome to my homepage!</p> + </> + ) +} + +export default Home +``` + +## Image Imports + +You can `import` images that live in your project. (Note that `require` is not supported—only `import`.) + +With direct `import`s, `width`, `height`, and `blurDataURL` will be automatically provided to the image component. Alt text is still needed separately. + +```js +import Image from 'next/image' +import profilePic from '../public/me.png' + +function Home() { + return ( + <> + <h1>My Homepage</h1> + <Image + src={profilePic} + alt="Picture of the author" + // width={500} automatically provided + // height={500} automatically provided + // blurDataURL="data:..." automatically provided + // Optionally allows to add a blurred version of the image while loading + // placeholder="blur" + /> + <p>Welcome to my homepage!</p> + </> + ) +} +``` + +For dynamic or remote images, you'll have to provide [`width`](/docs/api-reference/next/image#width), [`height`](/docs/api-reference/next/image#height) and [`blurDataURL`](/docs/api-reference/next/image#blurdataurl) manually. + +## Properties + +[View all properties](/docs/api-reference/next/image.md) available to the `next/image` component. + +## Configuration + +In addition to [using properties](/docs/api-reference/next/image.md) available to the `next/image` component, you can optionally configure Image Optimization for more advanced use cases via `next.config.js`. + +### Domains + +To enable Image Optimization for images hosted on an external website, use an absolute url for the Image `src` and specify which +`domains` are allowed to be optimized. This is needed to ensure that external urls can't be abused. When `loader` is set to an external image service, this option is ignored. + +```js +module.exports = { + images: { + domains: ['example.com'], + }, +} +``` + +### Loader + +If you want to use a cloud provider to optimize images instead of using the Next.js' built-in Image Optimization, you can configure the loader and path prefix. This allows you to use relative urls for the Image `src` and automatically generate the correct absolute url for your provider. + +```js +module.exports = { + images: { + loader: 'imgix', + path: 'https://example.com/myaccount/', + }, +} +``` + +The following Image Optimization cloud providers are included: + +- [Vercel](https://vercel.com): Works automatically when you deploy on Vercel, no configuration necessary. [Learn more](https://vercel.com/docs/next.js/image-optimization) +- [Imgix](https://www.imgix.com): `loader: 'imgix'` +- [Cloudinary](https://cloudinary.com): `loader: 'cloudinary'` +- [Akamai](https://www.akamai.com): `loader: 'akamai'` +- Custom: `loader: 'custom'` use a custom cloud provider by implementing the [`loader`](/docs/api-reference/next/image.md#loader) prop on the `next/image` component +- Default: Works automatically with `next dev`, `next start`, or a custom server + +If you need a different provider, you can use the [`loader`](/docs/api-reference/next/image.md#loader) prop with `next/image`. + +> The `next/image` component's default loader is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). However, other loader options will work. + +> The `next/image` component's default loader uses [`squoosh`](https://www.npmjs.com/package/@squoosh/lib) because it is quick to install and suitable for a development environment. When using `next start` in your production environment, it is strongly recommended that you install [`sharp`](https://www.npmjs.com/package/sharp) by running `yarn add sharp` in your project directory. This is not necessary for Vercel deployments, as `sharp` is installed automatically. + +## Caching + +The following describes the caching algorithm for the default [loader](#loader). For all other loaders, please refer to your cloud provider's documentation. + +Images are optimized dynamically upon request and stored in the `<distDir>/cache/images` directory. The optimized image file will be served for subsequent requests until the expiration is reached. When a request is made that matches a cached but expired file, the cached file is deleted before generating a new optimized image and caching the new file. + +The expiration (or rather Max Age) is defined by the upstream server's `Cache-Control` header. + +If `s-maxage` is found in `Cache-Control`, it is used. If no `s-maxage` is found, then `max-age` is used. If no `max-age` is found, then [`minimumCacheTTL`](#minimum-cache-ttl) is used. + +You can configure [`minimumCacheTTL`](#minimum-cache-ttl) to increase the cache duration when the upstream image does not include `max-age`. + +You can also configure [`deviceSizes`](#device-sizes) and [`imageSizes`](#device-sizes) to reduce the total number of possible generated images. + +## Advanced + +The following configuration is for advanced use cases and is usually not necessary. If you choose to configure the properties below, you will override any changes to the Next.js defaults in future updates. + +### Device Sizes + +In some cases, where you know the expected device widths from the users of your website, you can specify a list of device width breakpoints using the `deviceSizes` property. These widths are used when the [`next/image`](/docs/api-reference/next/image.md) component uses `layout="responsive"` or `layout="fill"` so that the correct image is served for the device visiting your website. + +If no configuration is provided, the default below is used. + +```js +module.exports = { + images: { + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + }, +} +``` + +### Image Sizes + +You can specify a list of image widths using the `imageSizes` property. These widths should be different (usually smaller) than the widths defined in `deviceSizes` because the arrays will be concatenated. These widths are used when the [`next/image`](/docs/api-reference/next/image.md) component uses `layout="fixed"` or `layout="intrinsic"`. + +If no configuration is provided, the default below is used. + +```js +module.exports = { + images: { + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + }, +} +``` + +### Minimum Cache TTL + +You can configure the time to live (TTL) in seconds for cached optimized images. In many cases, its better to use a [Static Image Import](#image-Imports) which will handle hashing file contents and caching the file forever. + +```js +module.exports = { + images: { + minimumCacheTTL: 60, + }, +} +``` + +If you need to add a `Cache-Control` header for the browser (not recommended), you can configure [`headers`](/docs/api-reference/next.config.js/headers) on the upstream image e.g. `/some-asset.jpg` not `/_next/image` itself. + +### Disable Static Imports + +The default behavior allows you to import static files such as `import icon from './icon.png` and then pass that to the `src` property. + +In some cases, you may wish to disable this feature if it conflicts with other plugins that expect the import to behave differently. + +You can disable static image imports with the following configuration below. + +```js +module.exports = { + images: { + disableStaticImages: true, + }, +} +``` + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/api-reference/next/image.md"> + <b>next/image</b> + <small>See all available properties for the Image component</small> + </a> +</div> diff --git a/docs/basic-features/layouts.md b/docs/basic-features/layouts.md new file mode 100644 index 0000000000000..ea9bfd6f1424e --- /dev/null +++ b/docs/basic-features/layouts.md @@ -0,0 +1,185 @@ +--- +description: Learn how to share components and state between Next.js pages with Layouts. +--- + +# Layouts + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/layout-component">layout-component</a></li> + </ul> +</details> + +The React model allows us to deconstruct a [page](/docs/basic-features/pages.md) into a series of components. Many of these components are often reused between pages. For example, you might have the same navigation bar and footer on every page. + +```jsx +// components/layout.js + +import Navbar from './navbar' +import Footer from './footer' + +export default function Layout({ children }) { + return ( + <> + <Navbar /> + <main>{children}</main> + <Footer /> + </> + ) +} +``` + +## Examples + +### Single Shared Layout with Custom App + +If you only have one layout for your entire application, you can create a [Custom App](/docs/advanced-features/custom-app.md) and wrap your application with the layout. Since the `<Layout />` component is re-used when changing pages, its component state will be preserved (e.g. input values). + +```jsx +// pages/_app.js + +import Layout from '../components/layout' + +export default function MyApp({ Component, pageProps }) { + return ( + <Layout> + <Component {...pageProps} /> + </Layout> + ) +} +``` + +### Per-Page Layouts + +If you need multiple layouts, you can add a property `getLayout` to your page, allowing you to return a React component for the layout. This allows you to define the layout on a _per-page basis_. Since we're returning a function, we can have complex nested layouts if desired. + +```jsx +// pages/index.js + +import Layout from '../components/layout' +import NestedLayout from '../components/nested-layout' + +export default function Page() { + return { + /** Your content */ + } +} + +Page.getLayout = function getLayout(page) { + return ( + <Layout> + <NestedLayout>{page}</NestedLayout> + </Layout> + ) +} +``` + +```jsx +// pages/_app.js + +export default function MyApp({ Component, pageProps }) { + // Use the layout defined at the page level, if available + const getLayout = Component.getLayout || ((page) => page) + + return getLayout(<Component {...pageProps} />) +} +``` + +When navigating between pages, we want to *persist* page state (input values, scroll position, etc) for a Single-Page Application (SPA) experience. + +This layout pattern enables state persistence because the React component tree is maintained between page transitions. With the component tree, React can understand which elements have changed to preserve state. + +> **Note**: This process is called [reconciliation](https://reactjs.org/docs/reconciliation.html), which is how React understands which elements have changed. + +### With TypeScript + +When using TypeScript, you must first create a new type for your pages which includes a `getLayout` function. Then, you must create a new type for your `AppProps` which overrides the `Component` property to use the previously created type. + +```tsx +// pages/index.tsx + +import type { ReactElement } from 'react' +import Layout from '../components/layout' +import NestedLayout from '../components/nested-layout' + +export default function Page() { + return { + /** Your content */ + } +} + +Page.getLayout = function getLayout(page: ReactElement) { + return ( + <Layout> + <NestedLayout>{page}</NestedLayout> + </Layout> + ) +} +``` + +```tsx +// pages/_app.tsx + +import type { ReactElement, ReactNode } from 'react' +import type { NextPage } from 'next' +import type { AppProps } from 'next/app' + +type NextPageWithLayout = NextPage & { + getLayout?: (page: ReactElement) => ReactNode +} + +type AppPropsWithLayout = AppProps & { + Component: NextPageWithLayout +} + +export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { + // Use the layout defined at the page level, if available + const getLayout = Component.getLayout ?? ((page) => page) + + return getLayout(<Component {...pageProps} />) +} +``` + +### Data Fetching + +Inside your layout, you can fetch data on the client-side using `useEffect` or a library like [SWR](https://swr.vercel.app/). Because this file is not a [Page](/docs/basic-features/pages.md), you cannot use `getStaticProps` or `getServerSideProps` currently. + +```jsx +// components/layout.js + +import useSWR from 'swr' +import Navbar from './navbar' +import Footer from './footer' + +export default function Layout({ children }) { + const { data, error } = useSWR('/api/navigation', fetcher) + + if (error) return <div>Failed to load</div> + if (!data) return <div>Loading...</div> + + return ( + <> + <Navbar links={data.links} /> + <main>{children}</main> + <Footer /> + </> + ) +} +``` + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/basic-features/pages.md"> + <b>Pages:</b> + <small>Learn more about what pages are in Next.js.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/advanced-features/custom-app.md"> + <b>Custom App:</b> + <small>Learn more about how Next.js initialize pages.</small> + </a> +</div> diff --git a/docs/basic-features/pages.md b/docs/basic-features/pages.md index dbf2f144a4655..7efbfc1382f1c 100644 --- a/docs/basic-features/pages.md +++ b/docs/basic-features/pages.md @@ -39,22 +39,31 @@ Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Importantly, Next.js lets you **choose** which pre-rendering form you'd like to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others. -We **recommend** using **Static Generation** over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN to boost performance. However, in some cases, Server-side Rendering might be the only option. +We **recommend** using **Static Generation** over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN with no extra configuration to boost performance. However, in some cases, Server-side Rendering might be the only option. -Finally, you can always use **Client-side Rendering** along with Static Generation or Server-side Rendering. That means some parts of a page can be rendered entirely by client side JavaScript. To learn more, take a look at the [Data Fetching](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side) documentation. +You can also use **Client-side Rendering** along with Static Generation or Server-side Rendering. That means some parts of a page can be rendered entirely by client side JavaScript. To learn more, take a look at the [Data Fetching](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side) documentation. ## Static Generation (Recommended) <details open> <summary><b>Examples</b></summary> <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter">Blog Starter using markdown files</a> (<a href="https://next-blog-starter.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.now.sh/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.now.sh/">Demo</a>)</li> - <li><a href="https://static-tweet.now.sh/">Static Tweet Demo</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-wordpress">WordPress Example</a> (<a href="https://next-blog-wordpress.vercel.app">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter">Blog Starter using markdown files</a> (<a href="https://next-blog-starter.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-strapi">Strapi Example</a> (<a href="https://next-blog-strapi.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prepr">Prepr Example</a> (<a href="https://next-blog-prepr.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-agilitycms">Agility CMS Example</a> (<a href="https://next-blog-agilitycms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic">Cosmic Example</a> (<a href="https://next-blog-cosmic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-buttercms">ButterCMS Example</a> (<a href="https://next-blog-buttercms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app/">Demo</a>)</li> + <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> </ul> </details> @@ -78,7 +87,7 @@ Note that this page does not need to fetch any external data to be pre-rendered. ### Static Generation with data -Some pages require fetching external data for pre-rendering. There are two scenarios, and one or both might apply. In each case, you can use a special function Next.js provides: +Some pages require fetching external data for pre-rendering. There are two scenarios, and one or both might apply. In each case, you can use these functions that Next.js provides: 1. Your page **content** depends on external data: Use `getStaticProps`. 2. Your page **paths** depend on external data: Use `getStaticPaths` (usually in addition to `getStaticProps`). @@ -116,7 +125,7 @@ export async function getStaticProps() { const res = await fetch('https://.../posts') const posts = await res.json() - // By returning { props: posts }, the Blog component + // By returning { props: { posts } }, the Blog component // will receive `posts` as a prop at build time return { props: { @@ -152,7 +161,9 @@ export async function getStaticPaths() { const posts = await res.json() // Get the paths we want to pre-render based on posts - const paths = posts.map((post) => `/posts/${post.id}`) + const paths = posts.map((post) => ({ + params: { id: post.id }, + })) // We'll pre-render only these paths at build time. // { fallback: false } means other routes should 404. diff --git a/docs/basic-features/script.md b/docs/basic-features/script.md new file mode 100644 index 0000000000000..ef688170332e4 --- /dev/null +++ b/docs/basic-features/script.md @@ -0,0 +1,165 @@ +--- +description: Next.js helps you optimize loading third-party scripts with the built-in next/script component. +--- + +# Script Component + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ------------------------- | +| `v11.0.0` | `next/script` introduced. | + +</details> + +The Next.js Script component enables developers to set the loading priority of third-party scripts to save developer time and improve loading performance. + +Websites often need third parties for things like analytics, ads, customer support widgets, and consent management. However, these scripts tend to be heavy on loading performance and can drag down the user experience. Developers often struggle to decide where to place them in an application for optimal loading. + +With `next/script`, you can define the `strategy` property and Next.js will optimize loading for the script: + +- `beforeInteractive`: For critical scripts that need to be fetched and executed **before** the page is interactive, such as bot detection and consent management. These scripts are injected into the initial HTML from the server and run before self-bundled JavaScript is executed. +- `afterInteractive` (**default**): For scripts that can fetch and execute **after** the page is interactive, such as tag managers and analytics. These scripts are injected on the client-side and will run after hydration. +- `lazyOnload` For scripts that can wait to load during idle time, such as chat support and social media widgets. + +> **Note:** +> +> - `<Script>` supports inline scripts with `afterInteractive` and `lazyOnload` strategy. +> - Inline scripts wrapped with `<Script>` _require an `id` attribute to be defined_ to track and optimize the script. + +## Usage + +Previously, you needed to define `script` tags inside the `Head` of your Next.js page. + +```js +// Before + +// pages/index.js +import Head from 'next/head' + +export default function Home() { + return ( + <> + <Head> + <script async src="https://www.google-analytics.com/analytics.js" /> + </Head> + </> + ) +} +``` + +Now, you use `next/script` in the body of your Next.js page. It has client-side functionality that decides when and how to load the remote script based on the `strategy`. + +> **Note:** +> +> - `next/script` **must not** be placed in either a `next/head` component or in `pages/_document.js`. + +```js +// After + +// pages/index.js +import Script from 'next/script' + +export default function Home() { + return ( + <> + <Script src="https://www.google-analytics.com/analytics.js" /> + </> + ) +} +``` + +## Examples + +### Loading Polyfills + +```js +import Script from 'next/script' + +export default function Home() { + return ( + <> + <Script + src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver" + strategy="beforeInteractive" + /> + </> + ) +} +``` + +### Lazy-Loading + +```js +import Script from 'next/script' + +export default function Home() { + return ( + <> + <Script + src="https://connect.facebook.net/en_US/sdk.js" + strategy="lazyOnload" + /> + </> + ) +} +``` + +### Executing Code After Loading (`onLoad`) + +```js +import Script from 'next/script' + +export default function Home() { + return ( + <> + <Script + id="stripe-js" + src="https://js.stripe.com/v3/" + onLoad={() => { + this.setState({ stripe: window.Stripe('pk_test_12345') }) + }} + /> + </> + ) +} +``` + +### Inline Scripts + +```js +import Script from 'next/script' + +<Script id="show-banner" strategy="lazyOnload"> + {`document.getElementById('banner').removeClass('hidden')`} +</Script> + +// or + +<Script + id="show-banner" + dangerouslySetInnerHTML={{ + __html: `document.getElementById('banner').removeClass('hidden')` + }} +/> +``` + +### Forwarding Attributes + +```js +import Script from 'next/script' + +export default function Home() { + return ( + <> + <Script + src="https://www.google-analytics.com/analytics.js" + id="analytics" + nonce="XUENAJFW" + data-test="analytics" + /> + </> + ) +} +``` diff --git a/docs/basic-features/static-file-serving.md b/docs/basic-features/static-file-serving.md index ffc0525641e82..c4234b7aa2f2e 100644 --- a/docs/basic-features/static-file-serving.md +++ b/docs/basic-features/static-file-serving.md @@ -6,20 +6,26 @@ description: Next.js allows you to serve static files, like images, in the publi Next.js can serve static files, like images, under a folder called `public` in the root directory. Files inside `public` can then be referenced by your code starting from the base URL (`/`). -For example, if you add an image to `public/my-image.png`, the following code will access the image: +For example, if you add an image to `public/me.png`, the following code will access the image: ```jsx -function MyImage() { - return <img src="/my-image.png" alt="my image" /> +import Image from 'next/image' + +function Avatar() { + return <Image src="/me.png" alt="me" width="64" height="64" /> } -export default MyImage +export default Avatar ``` -This folder is also useful for `robots.txt`, Google Site Verification, and any other static files (including `.html`)! +> Note: `next/image` requires Next.js 10 or later. + +This folder is also useful for `robots.txt`, `favicon.ico`, Google Site Verification, and any other static files (including `.html`)! > **Note**: Don't name the `public` directory anything else. The name cannot be changed and is the only directory used to serve static assets. > **Note**: Be sure to not have a static file with the same name as a file in the `pages/` directory, as this will result in an error. > -> Read more: <http://err.sh/next.js/conflicting-public-file-page> +> Read more: <https://nextjs.org/docs/messages/conflicting-public-file-page> + +> **Note**: Only assets that are in the `public` directory at [build time](/docs/api-reference/cli.md#build) will be served by Next.js. Files added at runtime won't be available. We recommend using a third party service like [AWS S3](https://aws.amazon.com/s3/) for persistent file storage. diff --git a/docs/basic-features/supported-browsers-features.md b/docs/basic-features/supported-browsers-features.md new file mode 100644 index 0000000000000..dcaf6b65eabce --- /dev/null +++ b/docs/basic-features/supported-browsers-features.md @@ -0,0 +1,49 @@ +--- +description: Browser support and which JavaScript features are supported by Next.js. +--- + +# Supported Browsers and Features + +Next.js supports **IE11 and all modern browsers** (Edge, Firefox, Chrome, Safari, Opera, et al) with no required configuration. + +## Polyfills + +We transparently inject polyfills required for IE11 compatibility. In addition, we also inject widely used polyfills, including: + +- [**fetch()**](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) — Replacing: `whatwg-fetch` and `unfetch`. +- [**URL**](https://developer.mozilla.org/en-US/docs/Web/API/URL) — Replacing: the [`url` package (Node.js API)](https://nodejs.org/api/url.html). +- [**Object.assign()**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) — Replacing: `object-assign`, `object.assign`, and `core-js/object/assign`. + +If any of your dependencies includes these polyfills, they’ll be eliminated automatically from the production build to avoid duplication. + +In addition, to reduce bundle size, Next.js will only load these polyfills for browsers that require them. The majority of the web traffic globally will not download these polyfills. + +### Server-Side Polyfills + +In addition to `fetch()` on the client-side, Next.js polyfills `fetch()` in the Node.js environment. You can use `fetch()` in your server code (such as `getStaticProps`/`getServerSideProps`) without using polyfills such as `isomorphic-unfetch` or `node-fetch`. + +### Custom Polyfills + +If your own code or any external npm dependencies require features not supported by your target browsers, you need to add polyfills yourself. + +In this case, you should add a top-level import for the **specific polyfill** you need in your [Custom `<App>`](/docs/advanced-features/custom-app.md) or the individual component. + +## JavaScript Language Features + +Next.js allows you to use the latest JavaScript features out of the box. In addition to [ES6 features](https://github.com/lukehoban/es6features), Next.js also supports: + +- [Async/await](https://github.com/tc39/ecmascript-asyncawait) (ES2017) +- [Object Rest/Spread Properties](https://github.com/tc39/proposal-object-rest-spread) (ES2018) +- [Dynamic `import()`](https://github.com/tc39/proposal-dynamic-import) (ES2020) +- [Optional Chaining](https://github.com/tc39/proposal-optional-chaining) (ES2020) +- [Nullish Coalescing](https://github.com/tc39/proposal-nullish-coalescing) (ES2020) +- [Class Fields](https://github.com/tc39/proposal-class-fields) and [Static Properties](https://github.com/tc39/proposal-static-class-features) (part of stage 3 proposal) +- and more! + +### TypeScript Features + +Next.js has built-in TypeScript support. [Learn more here](/docs/basic-features/typescript.md). + +### Customizing Babel Config (Advanced) + +You can customize babel configuration. [Learn more here](/docs/advanced-features/customizing-babel-config.md). diff --git a/docs/basic-features/typescript.md b/docs/basic-features/typescript.md index 56b8da646beb2..8eeab64ceafe5 100644 --- a/docs/basic-features/typescript.md +++ b/docs/basic-features/typescript.md @@ -11,9 +11,23 @@ description: Next.js supports TypeScript by default and has built-in types for p </ul> </details> -Next.js provides an integrated [TypeScript](https://www.typescriptlang.org/) experience out of the box, similar to an IDE. +Next.js provides an integrated [TypeScript](https://www.typescriptlang.org/) +experience out of the box, similar to an IDE. -To get started, create an empty `tsconfig.json` file in the root of your project: +## `create-next-app` support + +You can create a TypeScript project with [`create-next-app`](https://nextjs.org/docs/api-reference/create-next-app) using the `--ts, --typescript` flag like so: + +``` +npx create-next-app --ts +# or +yarn create next-app --typescript +``` + +## Existing projects + +To get started in an existing project, create an empty `tsconfig.json` file in +the root folder: ```bash touch tsconfig.json @@ -23,7 +37,7 @@ Next.js will automatically configure this file with default values. Providing yo > Next.js uses Babel to handle TypeScript, which has some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats), and some [compiler options are handled differently](https://babeljs.io/docs/en/babel-plugin-transform-typescript#typescript-compiler-options). -Then, run `next` (normally `npm run dev`) and Next.js will guide you through the installation of the required packages to finish the setup: +Then, run `next` (normally `npm run dev` or `yarn dev`) and Next.js will guide you through the installation of the required packages to finish the setup: ```bash npm run dev @@ -39,11 +53,13 @@ npm run dev You're now ready to start converting files from `.js` to `.tsx` and leveraging the benefits of TypeScript!. -> A file named `next-env.d.ts` will be created in the root of your project. This file ensures Next.js types are picked up by the TypeScript compiler. **You cannot remove it**, however, you can edit it (but you don't need to). +> A file named `next-env.d.ts` will be created in the root of your project. This file ensures Next.js types are picked up by the TypeScript compiler. **You cannot remove it or edit it** as it can change at any time. + +> TypeScript `strict` mode is turned off by default. When you feel comfortable with TypeScript, it's recommended to turn it on in your `tsconfig.json`. -> Next.js `strict` mode is turned off by default. When you feel comfortable with TypeScript, it's recommended to turn it on in your `tsconfig.json`. +> Instead of editing `next-env.d.ts`, you can include additional types by adding a new file e.g. `additional.d.ts` and then referencing it in the [`include`](https://www.typescriptlang.org/tsconfig#include) array in your `tsconfig.json`. -By default, Next.js reports TypeScript errors during development for pages you are actively working on. TypeScript errors for inactive pages **do not** block the development process. +By default, Next.js will do type checking as part of `next build`. We recommend using code editor type checking during development. If you want to silence the error reports, refer to the documentation for [Ignoring TypeScript errors](/docs/api-reference/next.config.js/ignoring-typescript-errors.md). @@ -74,7 +90,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { The following is an example of how to use the built-in types for API routes: ```ts -import { NextApiRequest, NextApiResponse } from 'next' +import type { NextApiRequest, NextApiResponse } from 'next' export default (req: NextApiRequest, res: NextApiResponse) => { res.status(200).json({ name: 'John Doe' }) @@ -84,7 +100,7 @@ export default (req: NextApiRequest, res: NextApiResponse) => { You can also type the response data: ```ts -import { NextApiRequest, NextApiResponse } from 'next' +import type { NextApiRequest, NextApiResponse } from 'next' type Data = { name: string @@ -97,15 +113,28 @@ export default (req: NextApiRequest, res: NextApiResponse<Data>) => { ## Custom `App` -If you have a [custom `App` ](/docs/advanced-features/custom-app), you can use the built-in type `AppProps` and change file name to `./pages/_app.tsx` like so: +If you have a [custom `App`](/docs/advanced-features/custom-app.md), you can use the built-in type `AppProps` and change file name to `./pages/_app.tsx` like so: ```ts -import { AppProps } from 'next/app' +// import App from "next/app"; +import type { AppProps /*, AppContext */ } from 'next/app' function MyApp({ Component, pageProps }: AppProps) { return <Component {...pageProps} /> } +// Only uncomment this method if you have blocking data requirements for +// every single page in your application. This disables the ability to +// perform automatic static optimization, causing every page in your app to +// be server-side rendered. +// +// MyApp.getInitialProps = async (appContext: AppContext) => { +// // calls page's `getInitialProps` and fills `appProps.pageProps` +// const appProps = await App.getInitialProps(appContext); + +// return { ...appProps } +// } + export default MyApp ``` @@ -114,3 +143,26 @@ export default MyApp Next.js automatically supports the `tsconfig.json` `"paths"` and `"baseUrl"` options. You can learn more about this feature on the [Module Path aliases documentation](/docs/advanced-features/module-path-aliases.md). + +## Type checking next.config.js + +The `next.config.js` file must be a JavaScript file as it does not get parsed by Babel or TypeScript, however you can add some type checking in your IDE using JSDoc as below: + +```js +// @ts-check + +/** + * @type {import('next').NextConfig} + **/ +const nextConfig = { + /* config options here */ +} + +module.exports = nextConfig +``` + +## Incremental type checking + +Since `v10.2.1` Next.js supports [incremental type checking](https://www.typescriptlang.org/tsconfig#incremental) when enabled in your `tsconfig.json`, this can help speed up type checking in larger applications. + +It is highly recommended to be on at least `v4.3.2` of TypeScript to experience the [best performance](https://devblogs.microsoft.com/typescript/announcing-typescript-4-3/#lazier-incremental) when leveraging this feature. diff --git a/docs/deployment.md b/docs/deployment.md index 16e4578170363..b4c30a3806163 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -6,20 +6,20 @@ description: Deploy your Next.js app to production with Vercel and other hosting ## Vercel (Recommended) -The easiest way to deploy Next.js to production is to use the **[Vercel platform](https://vercel.com)** from the creators of Next.js. [Vercel](https://vercel.com) is an all-in-one platform with Global CDN supporting static & JAMstack deployment and Serverless Functions. +The easiest way to deploy Next.js to production is to use the **[Vercel platform](https://vercel.com)** from the creators of Next.js. [Vercel](https://vercel.com) is a cloud platform for static sites, hybrid apps, and Serverless Functions. ### Getting started -If you haven’t already done so, push your Next.js app to a Git provider of your choice: [GitHub](http://github.com/), [GitLab](https://gitlab.com/), or [BitBucket](https://bitbucket.org/). Your repository can be private or public. +If you haven’t already done so, push your Next.js app to a Git provider of your choice: [GitHub](https://github.com/), [GitLab](https://gitlab.com/), or [BitBucket](https://bitbucket.org/). Your repository can be private or public. Then, follow these steps: 1. [Sign up to Vercel](https://vercel.com/signup) (no credit card is required). -2. After signing up, you’ll arrive on the [“Import Project”](https://vercel.com/import) page. Under “From Git Repository”, choose the Git provider you use and set up an integration. (Instructions: [GitHub](https://vercel.com/docs/v2/git-integrations/vercel-for-github) / [GitLab](https://vercel.com/docs/v2/git-integrations/vercel-for-gitlab) / [BitBucket](https://vercel.com/docs/v2/git-integrations/vercel-for-bitbucket)). -3. Once that’s set up, click “Import Project From …” and import your Next.js app. It auto-detects that your app is using Next.js and sets up the build configuration for you. No need to change anything — everything should work just fine! +2. After signing up, you’ll arrive on the [“Import Project”](https://vercel.com/new) page. Under “From Git Repository”, choose the Git provider you use and set up an integration. (Instructions: [GitHub](https://vercel.com/docs/git/vercel-for-github) / [GitLab](https://vercel.com/docs/git/vercel-for-gitlab) / [BitBucket](https://vercel.com/docs/git/vercel-for-bitbucket)). +3. Once that’s set up, click “Import Project From …” and import your Next.js app. It auto-detects that your app is using Next.js and sets up the build configuration for you. No need to change anything — everything should work fine! 4. After importing, it’ll deploy your Next.js app and provide you with a deployment URL. Click “Visit” to see your app in production. -Congratulations! You’ve just deployed your Next.js app! If you have questions, take a look at the [Vercel documentation](https://vercel.com/docs). +Congratulations! You’ve deployed your Next.js app! If you have questions, take a look at the [Vercel documentation](https://vercel.com/docs). > If you’re using a [custom server](/docs/advanced-features/custom-server.md), we strongly recommend migrating away from it (for example, by using [dynamic routing](/docs/routing/dynamic-routes.md)). If you cannot migrate, consider [other hosting options](#other-hosting-options). @@ -29,7 +29,7 @@ Let’s talk about the workflow we recommend using. [Vercel](https://vercel.com) - **Develop:** Write code in Next.js. Keep the development server running and take advantage of [React Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh). - **Preview:** Every time you push changes to a branch on GitHub / GitLab / BitBucket, Vercel automatically creates a new deployment with a unique URL. You can view them on GitHub when you open a pull request, or under “Preview Deployments” on your project page on Vercel. [Learn more about it here](https://vercel.com/features/deployment-previews). -- **Ship:** When you’re ready to ship, merge the pull request to your default branch (e.g. `master`). Vercel will automatically create a production deployment. +- **Ship:** When you’re ready to ship, merge the pull request to your default branch (e.g. `main`). Vercel will automatically create a production deployment. By using the DPS workflow, in addition to doing _code reviews_, you can do _deployment previews_. Each deployment creates a unique URL that can be shared or used for integration tests. @@ -40,16 +40,24 @@ By using the DPS workflow, in addition to doing _code reviews_, you can do _depl For example, the [hybrid pages](/docs/basic-features/pages.md) approach is fully supported out of the box. - Every page can either use [Static Generation](/docs/basic-features/pages.md#static-generation) or [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering). -- Pages that use [Static Generation](/docs/basic-features/pages.md#static-generation) and assets (JS, CSS, images, fonts, etc) will automatically be served from the [Vercel Smart CDN](https://vercel.com/smart-cdn), which is blazingly fast. +- Pages that use [Static Generation](/docs/basic-features/pages.md#static-generation) and assets (JS, CSS, images, fonts, etc) will automatically be served from [Vercel's Edge Network](https://vercel.com/docs/edge-network/overview), which is blazingly fast. - Pages that use [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering) and [API routes](/docs/api-routes/introduction.md) will automatically become isolated Serverless Functions. This allows page rendering and API requests to scale infinitely. ### Custom Domains, Environment Variables, Automatic HTTPS, and more -- **Custom Domains:** Once deployed on [Vercel](https://vercel.com), you can assign a custom domain to your Next.js app. Take a look at [our documentation here](https://vercel.com/docs/v2/custom-domains). -- **Environment Variables:** You can also set environment variables on Vercel. Take a look at [our documentation here](https://vercel.com/docs/v2/build-step#using-environment-variables-and-secrets). You can then [use those environment variables](/docs/api-reference/next.config.js/environment-variables.md) in your Next.js app. +- **Custom Domains:** Once deployed on [Vercel](https://vercel.com), you can assign a custom domain to your Next.js app. Take a look at [our documentation here](https://vercel.com/docs/custom-domains). +- **Environment Variables:** You can also set environment variables on Vercel. Take a look at [our documentation here](https://vercel.com/docs/environment-variables). You can then [use those environment variables](/docs/api-reference/next.config.js/environment-variables.md) in your Next.js app. - **Automatic HTTPS:** HTTPS is enabled by default (including custom domains) and doesn't require extra configuration. We auto-renew SSL certificates. - **More:** [Read our documentation](https://vercel.com/docs) to learn more about the Vercel platform. +## Automatic Updates + +When you deploy your Next.js application, you want to see the latest version without needing to reload. + +Next.js will automatically load the latest version of your application in the background when routing. For client-side navigation, `next/link` will temporarily function as a normal `<a>` tag. + +**Note:** If a new page (with an old version) has already been prefetched by `next/link`, Next.js will use the old version. Then, after either a full page refresh or multiple client-side page transitions, Next.js will show the latest version. + ## Other hosting options ### Node.js Server @@ -70,8 +78,67 @@ Make sure your `package.json` has the `"build"` and `"start"` scripts: `next build` builds the production application in the `.next` folder. After building, `next start` starts a Node.js server that supports [hybrid pages](/docs/basic-features/pages.md), serving both statically generated and server-side rendered pages. -### Static HTML Export +### Docker Image + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-docker">with-docker</a></li> + </ul> +</details> + +Next.js can be deployed to any hosting provider that supports [Docker](https://www.docker.com/) containers. You can use this approach when deploying to container orchestrators such as [Kubernetes](https://kubernetes.io/) or [HashiCorp Nomad](https://www.nomadproject.io/), or when running inside a single node in any cloud provider. + +Here is a multi-stage `Dockerfile` using `node:alpine` that you can use: + +```Dockerfile +# Install dependencies only when needed +FROM node:alpine AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app +COPY package.json yarn.lock ./ +RUN yarn install --frozen-lockfile + +# Rebuild the source code only when needed +FROM node:alpine AS builder +WORKDIR /app +COPY . . +COPY --from=deps /app/node_modules ./node_modules +RUN yarn build && yarn install --production --ignore-scripts --prefer-offline + +# Production image, copy all the files and run next +FROM node:alpine AS runner +WORKDIR /app -If you’d like to do a static HTML export of your Next.js app, follow the directions on [our documentation](/docs/advanced-features/static-html-export.md). By default, `next export` will generate an `out` directory, which can be served by any static hosting service or CDN. +ENV NODE_ENV production + +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 + +# You only need to copy next.config.js if you are NOT using the default configuration +# COPY --from=builder /app/next.config.js ./ +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/package.json ./package.json + +USER nextjs + +EXPOSE 3000 + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry. +# ENV NEXT_TELEMETRY_DISABLED 1 + +CMD ["yarn", "start"] +``` + +Make sure to place this Dockerfile in the root folder of your project. + +You can build your container with `docker build . -t my-next-js-app` and run it with `docker run -p 3000:3000 my-next-js-app`. + +### Static HTML Export -> We strongly recommend using [Vercel](https://vercel.com/) even if your Next.js app is fully static. [Vercel](https://vercel.com/) is optimized to make static Next.js apps blazingly fast. `next export` works with Zero Config deployments on Vercel. +If you’d like to do a static HTML export of your Next.js app, follow the directions on [our documentation](/docs/advanced-features/static-html-export.md). diff --git a/docs/faq.md b/docs/faq.md index 130f9dff79f97..8a1cec7254a91 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -4,13 +4,6 @@ description: Get to know more about Next.js with the frequently asked questions. # Frequently Asked Questions -<details> - <summary>What browsers are supported?</summary> - <p>Next.js supports IE11 and all modern browsers out of the box using <a href="https://new.babeljs.io/docs/en/next/babel-preset-env.html">@babel/preset-env</a>. In order to support IE11 Next.js adds a global Promise polyfill.</p> - - <p>In cases where your own code or any external npm dependencies you are using require features not supported by your target browsers you will need to implement polyfills. If you need to implement polyfills, the <a href="https://github.com/vercel/next.js/tree/canary/examples/with-polyfills">polyfills</a> example demonstrates the recommended approach.</p> -</details> - <details> <summary>Is this production ready?</summary> <p>Next.js has been powering <a href="https://vercel.com">https://vercel.com</a>  since its inception.</p> @@ -46,7 +39,7 @@ description: Get to know more about Next.js with the frequently asked questions. <details> <summary>How do I fetch data?</summary> - <p>It's up to you. You can use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">fetch API</a> or <a href="https://swr.now.sh/">SWR</a> inside your React components for remote data fetching; or use our <a href="/docs/basic-features/data-fetching.md">data fetching methods</a> for initial data population.</p> + <p>It's up to you. You can use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">fetch API</a> or <a href="https://swr.vercel.app/">SWR</a> inside your React components for remote data fetching; or use our <a href="/docs/basic-features/data-fetching.md">data fetching methods</a> for initial data population.</p> </details> <details> @@ -79,3 +72,8 @@ description: Get to know more about Next.js with the frequently asked questions. <p>As we were researching options for server-rendering React that didn’t involve a large number of steps, we came across <a href="https://github.com/facebookarchive/react-page">react-page</a> (now deprecated), a similar approach to Next.js by the creator of React Jordan Walke.</p> </details> + +<details> + <summary>Can I make a Next.js Progressive Web App (PWA)?</summary> + <p>Yes! Check out our <a href="https://github.com/vercel/next.js/tree/canary/examples/progressive-web-app">PWA Example</a> to see how it works.</p> +</details> diff --git a/docs/getting-started.md b/docs/getting-started.md index 32440f9c42cc6..ebe35fa1159a5 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -6,7 +6,7 @@ description: Get started with Next.js in the official documentation, and learn m Welcome to the Next.js documentation! -If you're new to Next.js we recommend that you start with the [learn course](https://nextjs.org/learn/basics/getting-started). +If you're new to Next.js we recommend that you start with the [learn course](https://nextjs.org/learn/basics/create-nextjs-app). The interactive course with quizzes will guide you through everything you need to know to use Next.js. @@ -14,7 +14,7 @@ If you have questions about anything related to Next.js, you're always welcome t #### System Requirements -- [Node.js 10.13](https://nodejs.org/) or later +- [Node.js 12.0](https://nodejs.org/) or later - MacOS, Windows (including WSL), and Linux are supported ## Setup @@ -27,33 +27,47 @@ npx create-next-app yarn create next-app ``` +If you want to start with a TypeScript project you can use the `--typescript` flag: + +```bash +npx create-next-app --typescript +# or +yarn create next-app --typescript +``` + After the installation is complete, follow the instructions to start the development server. Try editing `pages/index.js` and see the result on your browser. +For more information on how to use `create-next-app`, you can review the [`create-next-app` documentation](/docs/api-reference/create-next-app.md) + ## Manual Setup Install `next`, `react` and `react-dom` in your project: ```bash npm install next react react-dom +# or +yarn add next react react-dom ``` Open `package.json` and add the following `scripts`: ```json "scripts": { - "dev": "next", + "dev": "next dev", "build": "next build", - "start": "next start" + "start": "next start", + "lint": "next lint" } ``` These scripts refer to the different stages of developing an application: -- `dev` - Runs `next` which starts Next.js in development mode -- `build` - Runs `next build` which builds the application for production usage -- `start` - Runs `next start` which starts a Next.js production server +- `dev` - Runs [`next dev`](/docs/api-reference/cli.md#development) which starts Next.js in development mode +- `build` - Runs [`next build`](/docs/api-reference/cli.md#build) which builds the application for production usage +- `start` - Runs [`next start`](/docs/api-reference/cli.md#production) which starts a Next.js production server +- `lint` - Runs [`next lint`](/docs/api-reference/cli.md#lint) which sets up Next.js' built-in ESLint configuration -Next.js is built around the concept of pages. A page is a [React Component](https://reactjs.org/docs/components-and-props.html) exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the `pages` directory. +Next.js is built around the concept of [pages](/docs/basic-features/pages.md). A page is a [React Component](https://reactjs.org/docs/components-and-props.html) exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the `pages` directory. Pages are associated with a route based on their file name. For example `pages/about.js` is mapped to `/about`. You can even add dynamic route parameters with the filename. @@ -69,7 +83,7 @@ function HomePage() { export default HomePage ``` -To start developing your application run `npm run dev`. This starts the development server on `http://localhost:3000`. +To start developing your application run `npm run dev` or `yarn dev`. This starts the development server on `http://localhost:3000`. Visit `http://localhost:3000` to view your application. diff --git a/docs/going-to-production.md b/docs/going-to-production.md new file mode 100644 index 0000000000000..55d94ef2b5728 --- /dev/null +++ b/docs/going-to-production.md @@ -0,0 +1,129 @@ +--- +description: Before taking your Next.js application to production, here are some recommendations to ensure the best user experience. +--- + +# Going to Production + +Before taking your Next.js application to production, here are some recommendations to ensure the best user experience. + +## In General + +- Use [caching](#caching) wherever possible. +- Ensure your database and backend are deployed in the same region. +- Aim to ship the least amount of JavaScript possible. +- Defer loading heavy JavaScript bundles until needed. +- Ensure [logging](#logging) is set up. +- Ensure [error handling](#error-handling) is set up. +- Configure the [404](/docs/advanced-features/custom-error-page.md#404-page) (Not Found) and [500](/docs/advanced-features/custom-error-page.md#500-page) (Error) pages. +- Ensure you are [measuring performance](/docs/advanced-features/measuring-performance.md). +- Run [Lighthouse](https://developers.google.com/web/tools/lighthouse) to check for performance, best practices, accessibility, and SEO. For best results, use a production build of Next.js and use incognito in your browser so results aren't affected by extensions. +- Review [Supported Browsers and Features](/docs/basic-features/supported-browsers-features.md). +- Improve performance using: + - [`next/image` and Automatic Image Optimization](/docs/basic-features/image-optimization.md) + - [Automatic Font Optimization](/docs/basic-features/font-optimization.md) + - [Script Optimization](/docs/basic-features/script.md) + +## Caching + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/ssr-caching">ssr-caching</a></li> + </ul> +</details> + +Caching improves response times and reduces the number of requests to external services. Next.js automatically adds caching headers to immutable assets served from `/_next/static` including JavaScript, CSS, static images, and other media. + +``` +Cache-Control: public, max-age=31536000, immutable +``` + +`Cache-Control` headers set in `next.config.js` will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](/docs/basic-features/pages.md#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) function. If you're using `next/image`, there are also [specific caching rules](/docs/basic-features/image-optimization.md#caching) for the default Image Optimization loader. + +**Note:** When running your application locally with `next dev`, your headers are overwritten to prevent caching locally. + +``` +Cache-Control: no-cache, no-store, max-age=0, must-revalidate +``` + +You can also use caching headers inside `getServerSideProps` and API Routes for dynamic responses. For example, using [`stale-while-revalidate`](https://web.dev/stale-while-revalidate/). + +```jsx +// This value is considered fresh for ten seconds (s-maxage=10). +// If a request is repeated within the next 10 seconds, the previously +// cached value will still be fresh. If the request is repeated before 59 seconds, +// the cached value will be stale but still render (stale-while-revalidate=59). +// +// In the background, a revalidation request will be made to populate the cache +// with a fresh value. If you refresh the page, you will see the new value. +export async function getServerSideProps({ req, res }) { + res.setHeader( + 'Cache-Control', + 'public, s-maxage=10, stale-while-revalidate=59' + ) + + return { + props: {}, + } +} +``` + +> **Note:** Your deployment provider must support edge caching for dynamic responses. If you are self-hosting, you will need to add this logic to the edge yourself using a key/value store. If you are using Vercel, [edge caching works without configuration](https://vercel.com/docs/edge-network/caching). + +## Reducing JavaScript Size + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-dynamic-import">with-dynamic-import</a></li> + </ul> +</details> + +To reduce the amount of JavaScript sent to the browser, you can use the following tools to understand what is included inside each JavaScript bundle: + +- [Import Cost](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) – Display the size of the imported package inside VSCode. +- [Package Phobia](https://packagephobia.com/) – Find the cost of adding a new dev dependency to your project. +- [Bundle Phobia](https://bundlephobia.com/) - Analyze how much a dependency can increase bundle sizes. +- [Webpack Bundle Analyzer](https://github.com/vercel/next.js/tree/canary/packages/next-bundle-analyzer) – Visualize size of webpack output files with an interactive, zoomable treemap. + +Each file inside your `pages/` directory will automatically be code split into its own JavaScript bundle during `next build`. You can also use [Dynamic Imports](/docs/advanced-features/dynamic-import.md) to lazy-load components and libraries. For example, you might want to defer loading your modal code until a user clicks the open button. + +## Logging + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/Logflare/next-pino-logflare-logging-example">with-logging</a></li> + </ul> +</details> + +Since Next.js runs on both the client and server, there are multiple forms of logging supported: + +- `console.log` in the browser +- `stdout` on the server + +If you want a structured logging package, we recommend [Pino](https://www.npmjs.com/package/pino). If you're using Vercel, there are [pre-built logging integrations](https://vercel.com/integrations#logging) compatible with Next.js. + +## Error Handling + +<details open> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-sentry">with-sentry</a></li> + </ul> +</details> + +When an unhandled exception occurs, you can control the experience for your users with the [500 page](/docs/advanced-features/custom-error-page.md#500-page). We recommend customizing this to your brand instead of the default Next.js theme. + +You can also log and track exceptions with a tool like Sentry. [This example](https://github.com/vercel/next.js/tree/canary/examples/with-sentry) shows how to catch & report errors on both the client and server-side, using the Sentry SDK for Next.js. There's also a [Sentry integration for Vercel](https://vercel.com/integrations/sentry). + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/deployment.md"> + <b>Deployment:</b> + <small>Take your Next.js application to production.</small> + </a> +</div> diff --git a/docs/manifest.json b/docs/manifest.json index ab8dcf3a7c2aa..7f0c255b01dbd 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -14,17 +14,41 @@ "path": "/docs/basic-features/pages.md" }, { - "title": "Data fetching", + "title": "Data Fetching", "path": "/docs/basic-features/data-fetching.md" }, { "title": "Built-in CSS Support", "path": "/docs/basic-features/built-in-css-support.md" }, + { + "title": "Layouts", + "path": "/docs/basic-features/layouts.md" + }, + { + "title": "Image Optimization", + "path": "/docs/basic-features/image-optimization.md" + }, + { + "title": "Font Optimization", + "path": "/docs/basic-features/font-optimization.md" + }, + { + "title": "Script Optimization", + "path": "/docs/basic-features/script.md" + }, { "title": "Static File Serving", "path": "/docs/basic-features/static-file-serving.md" }, + { + "title": "Fast Refresh", + "path": "/docs/basic-features/fast-refresh.md" + }, + { + "title": "ESLint", + "path": "/docs/basic-features/eslint.md" + }, { "title": "TypeScript", "path": "/docs/basic-features/typescript.md" @@ -32,6 +56,10 @@ { "title": "Environment Variables", "path": "/docs/basic-features/environment-variables.md" + }, + { + "title": "Supported Browsers and Features", + "path": "/docs/basic-features/supported-browsers-features.md" } ] }, @@ -77,10 +105,22 @@ } ] }, + { + "title": "Going to Production", + "path": "/docs/going-to-production.md" + }, { "title": "Deployment", "path": "/docs/deployment.md" }, + { + "title": "Authentication", + "path": "/docs/authentication.md" + }, + { + "title": "Testing", + "path": "/docs/testing.md" + }, { "title": "Advanced Features", "routes": [ @@ -168,6 +208,22 @@ { "title": "Debugging", "path": "/docs/advanced-features/debugging.md" + }, + { + "title": "Source Maps", + "path": "/docs/advanced-features/source-maps.md" + }, + { + "title": "Codemods", + "path": "/docs/advanced-features/codemods.md" + }, + { + "title": "Internationalized Routing", + "path": "/docs/advanced-features/i18n-routing.md" + }, + { + "title": "Security Headers", + "path": "/docs/advanced-features/security-headers.md" } ] }, @@ -175,6 +231,27 @@ "title": "Upgrade Guide", "path": "/docs/upgrading.md" }, + { + "title": "Migrating to Next.js", + "routes": [ + { + "title": "Incrementally Adopting Next.js", + "path": "/docs/migrating/incremental-adoption.md" + }, + { + "title": "Migrating from Gatsby", + "path": "/docs/migrating/from-gatsby.md" + }, + { + "title": "Migrating from Create React App", + "path": "/docs/migrating/from-create-react-app.md" + }, + { + "title": "Migrating from React Router", + "path": "/docs/migrating/from-react-router.md" + } + ] + }, { "title": "FAQ", "path": "/docs/faq.md" } ] }, @@ -183,6 +260,10 @@ "heading": true, "routes": [ { "title": "CLI", "path": "/docs/api-reference/cli.md" }, + { + "title": "Create Next App", + "path": "/docs/api-reference/create-next-app.md" + }, { "title": "next/router", "path": "/docs/api-reference/next/router.md" @@ -191,6 +272,10 @@ "title": "next/link", "path": "/docs/api-reference/next/link.md" }, + { + "title": "next/image", + "path": "/docs/api-reference/next/image.md" + }, { "title": "next/head", "path": "/docs/api-reference/next/head.md" @@ -208,6 +293,10 @@ } ] }, + { + "title": "Static Optimization Indicator", + "path": "/docs/api-reference/next.config.js/static-optimization-indicator.md" + }, { "title": "next.config.js", "routes": [ @@ -219,6 +308,22 @@ "title": "Environment Variables", "path": "/docs/api-reference/next.config.js/environment-variables.md" }, + { + "title": "Base Path", + "path": "/docs/api-reference/next.config.js/basepath.md" + }, + { + "title": "Rewrites", + "path": "/docs/api-reference/next.config.js/rewrites.md" + }, + { + "title": "Redirects", + "path": "/docs/api-reference/next.config.js/redirects.md" + }, + { + "title": "Custom Headers", + "path": "/docs/api-reference/next.config.js/headers.md" + }, { "title": "Custom Page Extensions", "path": "/docs/api-reference/next.config.js/custom-page-extensions.md" @@ -227,10 +332,6 @@ "title": "CDN Support with Asset Prefix", "path": "/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md" }, - { - "title": "Build Target", - "path": "/docs/api-reference/next.config.js/build-target.md" - }, { "title": "Custom Webpack Config", "path": "/docs/api-reference/next.config.js/custom-webpack-config.md" @@ -239,10 +340,6 @@ "title": "Compression", "path": "/docs/api-reference/next.config.js/compression.md" }, - { - "title": "Static Optimization Indicator", - "path": "/docs/api-reference/next.config.js/static-optimization-indicator.md" - }, { "title": "Runtime Configuration", "path": "/docs/api-reference/next.config.js/runtime-configuration.md" @@ -255,6 +352,10 @@ "title": "Disabling ETag Generation", "path": "/docs/api-reference/next.config.js/disabling-etag-generation.md" }, + { + "title": "Disabling HTTP Keep-Alive", + "path": "/docs/api-reference/next.config.js/disabling-http-keep-alive.md" + }, { "title": "Setting a custom build directory", "path": "/docs/api-reference/next.config.js/setting-a-custom-build-directory.md" @@ -267,6 +368,10 @@ "title": "Configuring onDemandEntries", "path": "/docs/api-reference/next.config.js/configuring-onDemandEntries.md" }, + { + "title": "Ignoring ESLint", + "path": "/docs/api-reference/next.config.js/ignoring-eslint.md" + }, { "title": "Ignoring TypeScript Errors", "path": "/docs/api-reference/next.config.js/ignoring-typescript-errors.md" @@ -274,6 +379,14 @@ { "title": "exportPathMap", "path": "/docs/api-reference/next.config.js/exportPathMap.md" + }, + { + "title": "Trailing Slash", + "path": "/docs/api-reference/next.config.js/trailing-slash.md" + }, + { + "title": "React Strict Mode", + "path": "/docs/api-reference/next.config.js/react-strict-mode.md" } ] } diff --git a/docs/migrating/from-create-react-app.md b/docs/migrating/from-create-react-app.md new file mode 100644 index 0000000000000..07ff81fd30cd3 --- /dev/null +++ b/docs/migrating/from-create-react-app.md @@ -0,0 +1,239 @@ +--- +description: Learn how to transition an existing Create React App project to Next.js. +--- + +# Migrating from Create React App + +This guide will help you understand how to transition from an existing non-ejected Create React App project to Next.js. Migrating to Next.js will allow you to: + +- Choose which [data fetching](/docs/basic-features/data-fetching.md) strategy you want on a per-page basis. +- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching.md#incremental-static-regeneration) to update _existing_ pages by re-rendering them in the background as traffic comes in. +- Use [API Routes](/docs/api-routes/introduction.md). + +And more! Let’s walk through a series of steps to complete the migration. + +## Updating `package.json` and dependencies + +The first step towards migrating to Next.js is to update `package.json` and dependencies. You should: + +- Remove `react-scripts` (but keep `react` and `react-dom`). If you're using React Router, you can also remove `react-router-dom`. +- Install `next`. +- Add Next.js related commands to `scripts`. One is `next dev`, which runs a development server at `localhost:3000`. You should also add `next build` and `next start` for creating and starting a production build. + +Here's an example `package.json`: + +```json +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "latest", + "react-dom": "latest" + } +} +``` + +## Static Assets and Compiled Output + +Create React App uses the `public` directory for the [entry HTML file](https://create-react-app.dev/docs/using-the-public-folder), whereas Next.js uses it for static assets. It's possible to add static assets here, but Create React App recommends importing them directly from JavaScript files. + +- Move any images, fonts, or other static assets to `public`. +- Convert `index.html` (the entry point of your application) to Next.js. Any `<head>` code should be moved to a [custom `_document.js`](/docs/advanced-features/custom-document.md). Any shared layout between all pages should be moved to a [custom `_app.js`](/docs/advanced-features/custom-app.md). +- See [Styling](#styling) for CSS/Sass files. +- Add `.next` to `.gitignore`. + +## Creating Routes & Linking + +With Create React App, you're likely using React Router. Instead of using a third-party library, Next.js includes its own [file-system based routing](/docs/routing/introduction.md). + +- Convert all `Route` components to new files in the `pages` directory. +- For routes that require dynamic content (e.g. `/blog/:slug`), you can use [Dynamic Routes](/docs/routing/dynamic-routes.md) with Next.js (e.g. `pages/blog/[slug].js`). The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation)). + +For more information, see [Migrating from React Router](/docs/migrating/from-react-router.md). + +## Styling + +Next.js has built-in support for [CSS](/docs/basic-features/built-in-css-support.md), [Sass](/docs/basic-features/built-in-css-support.md#sass-support) and [CSS-in-JS](/docs/basic-features/built-in-css-support.md#css-in-js). + +With Create React App, you can import `.css` files directly inside React components. Next.js allows you to do the same, but requires these files to be [CSS Modules](/docs/basic-features/built-in-css-support.md). For global styles, you'll need a [custom `_app.js`](/docs/advanced-features/custom-app.md) to add a [global stylesheet](/docs/basic-features/built-in-css-support.md#adding-a-global-stylesheet). + +## Safely Accessing Web APIs + +With client-side rendered applications (like Create React App), you can access `window`, `localStorage`, `navigator`, and other [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) out of the box. + +Since Next.js uses [pre-rendering](/docs/basic-features/pages.md#pre-rendering), you'll need to safely access those Web APIs only when you're on the client-side. For example, the following code snippet will allow access to `window` only on the client-side. + +```jsx +if (typeof window !== 'undefined') { + // You now have access to `window` +} +``` + +A recommended way of accessing Web APIs safely is by using the [`useEffect`](https://reactjs.org/docs/hooks-effect.html) hook, which only executes client-side: + +```jsx +import { useEffect } from 'react' + +useEffect(() => { + // You now have access to `window` +}, []) +``` + +## Image Component and Image Optimization + +Since version **10.0.0**, Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). + +The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web. + +The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. + +Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Your build times aren't increased, whether shipping 10 images or 10 million images. + +```jsx +import Image from 'next/image' + +export default function Home() { + return ( + <> + <h1>My Homepage</h1> + <Image + src="/me.png" + alt="Picture of the author" + width={500} + height={500} + /> + <p>Welcome to my homepage!</p> + </> + ) +} +``` + +## Environment Variables + +Next.js has support for `.env` [Environment Variables](/docs/basic-features/environment-variables.md) similar to Create React App. The main different is the prefix used to expose environment variables on the client-side. + +- Change all environment variables with the `REACT_APP_` prefix to `NEXT_PUBLIC_`. +- Server-side environment variables will be available at build-time and in [API Routes](/docs/api-routes/introduction.md). + +## Search Engine Optimization + +Most Create React App examples use `react-helmet` to assist with adding `meta` tags for proper SEO. With Next.js, we use [`next/head`](/docs/api-reference/next/head.md) to add `meta` tags to your `<head />` element. For example, here's an SEO component with Create React App: + +```jsx +// src/components/seo.js + +import { Helmet } from 'react-helmet' + +export default function SEO({ description, title, siteTitle }) { + return ( + <Helmet + title={title} + titleTemplate={siteTitle ? `%s | ${siteTitle}` : null} + meta={[ + { + name: `description`, + content: description, + }, + { + property: `og:title`, + content: title, + }, + { + property: `og:description`, + content: description, + }, + { + property: `og:type`, + content: `website`, + }, + { + name: `twitter:card`, + content: `summary`, + }, + { + name: `twitter:creator`, + content: twitter, + }, + { + name: `twitter:title`, + content: title, + }, + { + name: `twitter:description`, + content: description, + }, + ]} + /> + ) +} +``` + +And here's the same example using Next.js. + +```jsx +// src/components/seo.js + +import Head from 'next/head' + +export default function SEO({ description, title, siteTitle }) { + return ( + <Head> + <title>{`${title} | ${siteTitle}`} + + + + + + + + + + + ) +} +``` + +## Single-Page App (SPA) + +If you want to move your existing Create React App to Next.js and keep a Single-Page App, you can move your old application's entry point to an [Optional Catch-All Route](/docs/routing/dynamic-routes.md#optional-catch-all-routes) named `pages/[[...app]].js`. + +```jsx +// pages/[[...app]].js + +import { useState, useEffect } from 'react' +import CreateReactAppEntryPoint from '../components/app' + +function App() { + const [isMounted, setIsMounted] = useState(false) + + useEffect(() => { + setIsMounted(true) + }, []) + + if (!isMounted) { + return null + } + + return +} + +export default App +``` + +## Ejected Create React App + +If you've ejected Create React App, here are some things to consider: + +- If you have custom file loaders set up for CSS, Sass, or other assets, this is all built-in with Next.js. +- If you've manually added [new JavaScript features](/docs/basic-features/supported-browsers-features.md#javascript-language-features) (e.g. Optional Chaining) or [Polyfills](/docs/basic-features/supported-browsers-features.md#polyfills), check to see what's included by default with Next.js. +- If you have a custom code splitting setup, you can remove that. Next.js has automatic code splitting on a [per-page basis](/docs/basic-features/pages.md). +- You can [customize your PostCSS setup](/docs/advanced-features/customizing-postcss-config.md#default-behavior) with Next.js without ejecting from the framework. +- You should reference the default [Babel config](/docs/advanced-features/customizing-babel-config.md) and [Webpack config](/docs/api-reference/next.config.js/custom-webpack-config.md) of Next.js to see what's included by default. + +## Learn More + +You can learn more about Next.js by completing our [starter tutorial](https://nextjs.org/learn/basics/create-nextjs-app). If you have questions or if this guide didn't work for you, feel free to reach out to our community on [GitHub Discussions](https://github.com/vercel/next.js/discussions). diff --git a/docs/migrating/from-gatsby.md b/docs/migrating/from-gatsby.md new file mode 100644 index 0000000000000..4601a7850ad8f --- /dev/null +++ b/docs/migrating/from-gatsby.md @@ -0,0 +1,312 @@ +--- +description: Learn how to transition an existing Gatsby project to Next.js. +--- + +# Migrating from Gatsby + +This guide will help you understand how to transition from an existing Gatsby project to Next.js. Migrating to Next.js will allow you to: + +- Choose which [data fetching](/docs/basic-features/data-fetching.md) strategy you want on a per-page basis. +- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching.md#incremental-static-regeneration) to update _existing_ pages by re-rendering them in the background as traffic comes in. +- Use [API Routes](/docs/api-routes/introduction.md). + +And more! Let’s walk through a series of steps to complete the migration. + +## Updating `package.json` and dependencies + +The first step towards migrating to Next.js is to update `package.json` and dependencies. You should: + +- Remove all Gatsby-related packages (but keep `react` and `react-dom`). +- Install `next`. +- Add Next.js related commands to `scripts`. One is `next dev`, which runs a development server at `localhost:3000`. You should also add `next build` and `next start` for creating and starting a production build. + +Here's an example `package.json` ([view diff](https://github.com/leerob/gatsby-to-nextjs/pull/1/files#diff-b9cfc7f2cdf78a7f4b91a753d10865a2)): + +```json +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "latest", + "react-dom": "latest" + } +} +``` + +## Static Assets and Compiled Output + +Gatsby uses the `public` directory for the compiled output, whereas Next.js uses it for static assets. Here are the steps for migration ([view diff](https://github.com/leerob/gatsby-to-nextjs/pull/1/files#diff-a084b794bc0759e7a6b77810e01874f2)): + +- Remove `.cache/` and `public` from `.gitignore` and delete both directories. +- Rename Gatsby’s `static` directory as `public`. +- Add `.next` to `.gitignore`. + +## Creating Routes + +Both Gatsby and Next support a `pages` directory, which uses [file-system based routing](/docs/routing/introduction.md). Gatsby's directory is `src/pages`, which is also [supported by Next.js](/docs/advanced-features/src-directory.md). + +Gatsby creates dynamic routes using the `createPages` API inside of `gatsby-node.js`. With Next, we can use [Dynamic Routes](/docs/routing/dynamic-routes.md) inside of `pages` to achieve the same effect. Rather than having a `template` directory, you can use the React component inside your dynamic route file. For example: + +- **Gatsby:** `createPages` API inside `gatsby-node.js` for each blog post, then have a template file at `src/templates/blog-post.js`. +- **Next:** Create `pages/blog/[slug].js` which contains the blog post template. The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation)). + +## Styling + +With Gatsby, global CSS imports are included in `gatsby-browser.js`. With Next, you should create a [custom `_app.js`](/docs/advanced-features/custom-app.md) for global CSS. When migrating, you can copy over your CSS imports directly and update the relative file path, if necessary. Next.js has [built-in CSS support](/docs/basic-features/built-in-css-support.md). + +## Links + +The Gatsby `Link` and Next.js [`Link`](/docs/api-reference/next/link.md) component have a slightly different API. + +```jsx +// Gatsby + +import { Link } from 'gatsby' + +export default function Home() { + return blog +} +``` + +```jsx +// Next.js + +import Link from 'next/link' + +export default function Home() { + return ( + + blog + + ) +} +``` + +Update any import statements, switch `to` to `href`, and add an `` tag as a child of the element. + +## Data Fetching + +The largest difference between Gatsby and Next.js is how data fetching is implemented. Gatsby is opinionated with GraphQL being the default strategy for retrieving data across your application. With Next.js, you get to choose which strategy you want (GraphQL is one supported option). + +Gatsby uses the `graphql` tag to query data in the pages of your site. This may include local data, remote data, or information about your site configuration. Gatsby only allows the creation of static pages. With Next.js, you can choose on a [per-page basis](/docs/basic-features/pages.md) which [data fetching strategy](/docs/basic-features/data-fetching.md) you want. For example, `getServerSideProps` allows you to do server-side rendering. If you wanted to generate a static page, you'd export `getStaticProps` / `getStaticPaths` inside the page, rather than using `pageQuery`. For example: + +```js +// src/pages/[slug].js + +// Install remark and remark-html +import remark from 'remark' +import html from 'remark-html' +import { getPostBySlug, getAllPosts } from '../lib/blog' + +export async function getStaticProps({ params }) { + const post = getPostBySlug(params.slug) + const markdown = await remark() + .use(html) + .process(post.content || '') + const content = markdown.toString() + + return { + props: { + ...post, + content, + }, + } +} + +export async function getStaticPaths() { + const posts = getAllPosts() + + return { + paths: posts.map((post) => { + return { + params: { + slug: post.slug, + }, + } + }), + fallback: false, + } +} +``` + +You'll commonly see Gatsby plugins used for reading the file system (`gatsby-source-filesystem`), handling markdown files (`gatsby-transformer-remark`), and so on. For example, the popular starter blog example has [15 Gatsby specific packages](https://github.com/gatsbyjs/gatsby-starter-blog/blob/master/package.json). Next takes a different approach. It includes common features directly inside the framework, and gives the user full control over integrations with external packages. For example, rather than abstracting reading from the file system to a plugin, you can use the native Node.js `fs` package inside `getStaticProps` / `getStaticPaths` to read from the file system. + +```js +// src/lib/blog.js + +// Install gray-matter and date-fns +import matter from 'gray-matter' +import { parseISO, format } from 'date-fns' +import fs from 'fs' +import { join } from 'path' + +// Add markdown files in `src/content/blog` +const postsDirectory = join(process.cwd(), 'src', 'content', 'blog') + +export function getPostBySlug(slug) { + const realSlug = slug.replace(/\.md$/, '') + const fullPath = join(postsDirectory, `${realSlug}.md`) + const fileContents = fs.readFileSync(fullPath, 'utf8') + const { data, content } = matter(fileContents) + const date = format(parseISO(data.date), 'MMMM dd, yyyy') + + return { slug: realSlug, frontmatter: { ...data, date }, content } +} + +export function getAllPosts() { + const slugs = fs.readdirSync(postsDirectory) + const posts = slugs.map((slug) => getPostBySlug(slug)) + + return posts +} +``` + +## Image Component and Image Optimization + +Since version **10.0.0**, Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). + +The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `` element, evolved for the modern web. + +The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. + +### Migrating from Gatsby Image + +Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Unlike static site generators and static-only solutions, your build times aren't increased, whether shipping 10 images or 10 million images. + +This means you can remove common Gatsby plugins like: + +- `gatsby-image` +- `gatsby-transformer-sharp` +- `gatsby-plugin-sharp` + +Instead, use the built-in [`next/image`](/docs/api-reference/next/image.md) component and [Automatic Image Optimization](/docs/basic-features/image-optimization.md). + +```jsx +import Image from 'next/image' + +export default function Home() { + return ( + <> +

My Homepage

+ Picture of the author +

Welcome to my homepage!

+ + ) +} +``` + +## Site Configuration + +With Gatsby, your site's metadata (name, description, etc) is located inside `gatsby-config.js`. This is then exposed through the GraphQL API and consumed through a `pageQuery` or a static query inside a component. + +With Next.js, we recommend creating a config file similar to below. You can then import this file anywhere without having to use GraphQL to access your site's metadata. + +```js +// src/config.js + +export default { + title: 'Starter Blog', + author: { + name: 'Lee Robinson', + summary: 'who loves Next.js.', + }, + description: 'A starter blog converting Gatsby -> Next.', + social: { + twitter: 'leeerob', + }, +} +``` + +## Search Engine Optimization + +Most Gatsby examples use `react-helmet` to assist with adding `meta` tags for proper SEO. With Next.js, we use [`next/head`](/docs/api-reference/next/head.md) to add `meta` tags to your `` element. For example, here's an SEO component with Gatsby: + +```js +// src/components/seo.js + +import { Helmet } from 'react-helmet' + +export default function SEO({ description, title, siteTitle }) { + return ( + + ) +} +``` + +And here's the same example using Next.js, including reading from a site config file. + +```js +// src/components/seo.js + +import Head from 'next/head' +import config from '../config' + +export default function SEO({ description, title }) { + const siteTitle = config.title + + return ( + + {`${title} | ${siteTitle}`} + + + + + + + + + + + ) +} +``` + +## Learn more + +Take a look at [this pull request](https://github.com/leerob/gatsby-to-nextjs/pull/1) for more details on how an app can be migrated from Gatsby to Next.js. If you have questions or if this guide didn't work for you, feel free to reach out to our community on [GitHub Discussions](https://github.com/vercel/next.js/discussions). diff --git a/docs/migrating/from-react-router.md b/docs/migrating/from-react-router.md new file mode 100644 index 0000000000000..a81482f5e1ec3 --- /dev/null +++ b/docs/migrating/from-react-router.md @@ -0,0 +1,175 @@ +--- +description: Learn how to migrate from React Router to file-system based routes with Next.js. +--- + +# Migrating from React Router + +This guide will help you understand how to transition from [React Router](https://reactrouter.com) to [file-system based](/docs/routing/introduction.md) routes with Next.js. Using [`next/link`](/docs/api-reference/next/link.md) and [`next/router`](/docs/api-reference/next/router.md) will allow you to: + +- Decrease bundle size by removing React Router as a dependency. +- Define your application routes through the file system. +- Utilize the latest improvements to the Next.js framework. + +## Basics + +First, uninstall React Router. You'll be migrating to the built-in routing with Next.js. + +```jsx +npm uninstall react-router-dom +``` + +The `Link` component for performing client-side route transitions is slightly different from React Router. + +```jsx +// Before (React Router) +import { Link } from 'react-router-dom' + +export default function App() { + return About +} + +// After (Next.js) +import Link from 'next/link' + +export default function App() { + return ( + +
About + + ) +} +``` + +Most React applications that use React Router have a top-level navigation file, containing a list of routes. For example: + +```jsx +import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' + +export default function App() { + return ( + + + +

About

+
+ +

Blog

+
+ +

Home

+
+
+
+ ) +} +``` + +With Next.js, you can express the same application structure in the file system. When a file is added to the [`pages`](/docs/basic-features/pages.md) directory it's automatically available as a route. + +- `pages/about.js` → `/about` +- `pages/blog.js` → `/blog` +- `pages/index.js` → `/` + +## Nested Routes + +In the example below, routes like `/blog/my-post` would render the `Post` component. If a slug was not provided, it would render the list of all blog posts. + +```jsx +import { + BrowserRouter as Router, + Switch, + Route, + useRouteMatch, + useParams, +} from 'react-router-dom' + +export default function Blog() { + // Nested route under /blog + const match = useRouteMatch() + + return ( + + + + + + +

All Blog Posts

+
+
+
+ ) +} + +function Post() { + const { slug } = useParams() + return

Post Slug: {slug}

+} +``` + +Rather than using the `:slug` syntax inside your `Route` component, Next.js uses the `[slug]` syntax in the file name for [Dynamic Routes](/docs/routing/dynamic-routes.md). We can transform this to Next.js by creating two new files, `pages/blog/index.js` (showing all pages) and `pages/blog/[slug].js` (showing an individual post). + +```jsx +// pages/blog/index.js + +export default function Blog() { + return

All Blog Posts

+} + +// pages/blog/[slug].js + +import { useRouter } from 'next/router' + +export default function Post() { + const router = useRouter() + const { slug } = router.query + + return

Post Slug: {slug}

+} +``` + +## Server Rendering + +Next.js has built-in support for [Server-side Rendering](/docs/basic-features/pages#server-side-rendering.md). This means you can remove any instances of `StaticRouter` in your code. + +## Code Splitting + +Next.js has built-in support for [Code Splitting](https://reactrouter.com/web/guides/code-splitting). This means you can remove any instances of: + +- `@loadable/server`, `@loadable/babel-plugin`, and `@loadable/webpack-plugin` +- Modifications to your `.babelrc` for `@loadable/babel-plugin` + +Each file inside your `pages/` directory will be code split into its own JavaScript bundle during the build process. Next.js [also supports](/docs/basic-features/supported-browsers-features.md#javascript-language-features) ES2020 dynamic `import()` for JavaScript. With it you can import JavaScript modules dynamically and work with them. They also work with SSR. + +For more information, read about [Dynamic Imports](https://nextjs.org/docs/advanced-features/dynamic-import). + +## Scroll Restoration + +Next.js has built-in support for [Scroll Restoration](https://reactrouter.com/web/guides/scroll-restoration). This means you can remove any custom `ScrollToTop` components you have defined. + +The default behavior of `next/link` and `next/router` is to scroll to the top of the page. You can also [disable this](https://nextjs.org/docs/api-reference/next/link#disable-scrolling-to-the-top-of-the-page) if you prefer. + +## Learn More + +For more information on what to do next, we recommend the following sections: + + + + + + diff --git a/docs/migrating/incremental-adoption.md b/docs/migrating/incremental-adoption.md new file mode 100644 index 0000000000000..5330d8bc17dc5 --- /dev/null +++ b/docs/migrating/incremental-adoption.md @@ -0,0 +1,93 @@ +--- +description: Learn different strategies for incrementally adopting Next.js into your development workflow. +--- + +# Incrementally Adopting Next.js + +
+ Examples + +
+ +Next.js has been designed for gradual adoption. With Next.js, you can continue using your existing code and add as much (or as little) React as you need. By starting small and incrementally adding more pages, you can prevent derailing feature work by avoiding a complete rewrite. + +## Strategies + +### Subpath + +The first strategy is to configure your server or proxy such that, everything under a specific subpath points to a Next.js app. For example, your existing website might be at `example.com`, and you might configure your proxy such that `example.com/store` serves a Next.js e-commerce store. + +Using [`basePath`](/docs/api-reference/next.config.js/basepath.md), you can configure your Next.js application's assets and links to automatically work with your new subpath `/store`. Since each page in Next.js is its own [standalone route](/docs/routing/introduction.md), pages like `pages/products.js` will route to `example.com/store/products` in your application. + +```jsx +// next.config.js + +module.exports = { + basePath: '/store', +} +``` + +To learn more about `basePath`, take a look at our [documentation](/docs/api-reference/next.config.js/basepath.md). + +> This feature was introduced in [Next.js 9.5](https://nextjs.org/blog/next-9-5) and up. If you’re using older versions of Next.js, please upgrade before trying it out. + +### Rewrites + +The second strategy is to create a new Next.js app that points to the root URL of your domain. Then, you can use [`rewrites`](/docs/api-reference/next.config.js/rewrites.md) inside `next.config.js` to have some subpaths to be proxied to your existing app. + +For example, let's say you created a Next.js app to be served from `example.com` with the following `next.config.js`. Now, requests for the pages you’ve added to this Next.js app (e.g. `/about` if you’ve added `pages/about.js`) will be handled by Next.js, and requests for any other route (e.g. `/dashboard`) will be proxied to `proxy.example.com`. + +```jsx +// next.config.js + +module.exports = { + async rewrites() { + return { + // After checking all Next.js pages (including dynamic routes) + // and static files we proxy any other requests + fallback: [ + { + source: '/:path*', + destination: `https://proxy.example.com/:path*`, + }, + ], + } + + // For versions of Next.js < v10.1 you can use a no-op rewrite instead + return [ + // we need to define a no-op rewrite to trigger checking + // all pages/static files before we attempt proxying + { + source: '/:path*', + destination: '/:path*', + }, + { + source: '/:path*', + destination: `https://proxy.example.com/:path*`, + }, + ] + }, +} +``` + +To learn more about rewrites, take a look at our [documentation](/docs/api-reference/next.config.js/rewrites.md). + +> This feature was introduced in [Next.js 9.5](https://nextjs.org/blog/next-9-5) and up. If you’re using older versions of Next.js, please upgrade before trying it out. + +### Micro-Frontends with Monorepos and Subdomains + +Next.js and [Vercel](https://vercel.com) make it straightforward to adopt [micro-frontends](https://martinfowler.com/articles/micro-frontends.html) and deploy as a [Monorepo](https://vercel.com/blog/monorepos). This allows you to use [subdomains](https://en.wikipedia.org/wiki/Subdomain) to adopt new applications incrementally. Some benefits of micro-frontends: + +- Smaller, more cohesive and maintainable codebases. +- More scalable organizations with decoupled, autonomous teams. +- The ability to upgrade, update, or even rewrite parts of the frontend in a more incremental fashion. + +Once your monorepo is set up, push changes to your Git repository as usual and you'll see the commits deployed to the Vercel projects you've connected. + +## Conclusion + +To learn more, read about [subpaths](/docs/api-reference/next.config.js/basepath.md) and [rewrites](/docs/api-reference/next.config.js/rewrites.md) or [deploy an example with micro-frontends](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-zones&project-name=with-zones&repository-name=with-zones). diff --git a/docs/routing/dynamic-routes.md b/docs/routing/dynamic-routes.md index f67edd6ee3cd7..9f0b7f66517f4 100644 --- a/docs/routing/dynamic-routes.md +++ b/docs/routing/dynamic-routes.md @@ -54,7 +54,37 @@ Multiple dynamic route segments work the same way. The page `pages/post/[pid]/[c { "pid": "abc", "comment": "a-comment" } ``` -Client-side navigations to a dynamic route can be handled with [`next/link`](/docs/api-reference/next/link.md#dynamic-routes). +Client-side navigations to dynamic routes are handled with [`next/link`](/docs/api-reference/next/link.md). If we wanted to have links to the routes used above it will look like this: + +```jsx +import Link from 'next/link' + +function Home() { + return ( + + ) +} + +export default Home +``` + +Read our docs for [Linking between pages](/docs/routing/introduction.md#linking-between-pages) to learn more. ### Catch all routes @@ -83,14 +113,14 @@ And in the case of `/post/a/b`, and any other matching path, new parameters will { "slug": ["a", "b"] } ``` -> A good example of catch all routes is the Next.js docs, a single page called [pages/docs/[...slug].js](https://github.com/zeit/next-site/blob/master/pages/docs/%5B...slug%5D.js) takes care of all the docs you're currently looking at. - ### Optional catch all routes Catch all routes can be made optional by including the parameter in double brackets (`[[...slug]]`). For example, `pages/post/[[...slug]].js` will match `/post`, `/post/a`, `/post/a/b`, and so on. +The main difference between catch all and optional catch all routes is that with optional, the route without the parameter is also matched (`/post` in the example above). + The `query` objects are as follows: ```json @@ -108,3 +138,21 @@ The `query` objects are as follows: - Pages that are statically optimized by [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) will be hydrated without their route parameters provided, i.e `query` will be an empty object (`{}`). After hydration, Next.js will trigger an update to your application to provide the route parameters in the `query` object. + +## Related + +For more information on what to do next, we recommend the following sections: + + + + diff --git a/docs/routing/imperatively.md b/docs/routing/imperatively.md index ba4659d889f28..7dbbf7a16a42d 100644 --- a/docs/routing/imperatively.md +++ b/docs/routing/imperatively.md @@ -1,5 +1,5 @@ --- -description: Client-side navigations are also possible using the Router API instead of the Link component. Learn more here. +description: Client-side navigations are also possible using the Next.js Router instead of the Link component. Learn more here. --- # Imperatively @@ -11,18 +11,18 @@ description: Client-side navigations are also possible using the Router API inst -[`next/link`](/docs/api-reference/next/link.md) should be able to cover most of your routing needs, but you can also do client-side navigations without it, take a look at the [Router API documentation](/docs/api-reference/next/router.md#router-api). +[`next/link`](/docs/api-reference/next/link.md) should be able to cover most of your routing needs, but you can also do client-side navigations without it, take a look at the [documentation for `next/router`](/docs/api-reference/next/router.md). -The following example shows the basic usage of the Router API: +The following example shows how to do basic page navigations with [`useRouter`](/docs/api-reference/next/router.md#useRouter): ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' function ReadMore() { + const router = useRouter() + return ( -
- Click Router.push('/about')}>here to read more -
+ router.push('/about')}>Click here to read more ) } diff --git a/docs/routing/introduction.md b/docs/routing/introduction.md index e16af502457c6..575c65db1792f 100644 --- a/docs/routing/introduction.md +++ b/docs/routing/introduction.md @@ -32,9 +32,11 @@ To match a dynamic segment you can use the bracket syntax. This allows you to ma - `pages/[username]/settings.js` → `/:username/settings` (`/foo/settings`) - `pages/post/[...all].js` → `/post/*` (`/post/2020/id/title`) +> Check out the [Dynamic Routes documentation](/docs/routing/dynamic-routes.md) to learn more about how they work. + ## Linking between pages -The Next.js router allows you to do client-side route transitions between pages, similarly to a single-page application. +The Next.js router allows you to do client-side route transitions between pages, similar to a single-page application. A React component called `Link` is provided to do this client-side route transition. @@ -54,6 +56,11 @@ function Home() { About Us +
  • + + Blog Post + +
  • ) } @@ -61,38 +68,56 @@ function Home() { export default Home ``` -When linking to a route with [dynamic path segments](/docs/routing/dynamic-routes.md) you have to provide `href` and `as` to make sure the router knows which JavaScript file to load. +In the example above we have multiple links, each one maps a path (`href`) to a known page: + +- `/` → `pages/index.js` +- `/about` → `pages/about.js` +- `/blog/hello-world` → `pages/blog/[slug].js` + +Any `` in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). The corresponding data for [server-rendered](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) routes is _not_ prefetched. -- `href` - The name of the page in the `pages` directory. For example `/blog/[slug]`. -- `as` - The url that will be shown in the browser. For example `/blog/hello-world`. +### Linking to dynamic paths + +You can also use interpolation to create the path, which comes in handy for [dynamic route segments](#dynamic-route-segments). For example, to show a list of posts which have been passed to the component as a prop: ```jsx import Link from 'next/link' -function Home() { +function Posts({ posts }) { return ( ) } -export default Home +export default Posts ``` -The `as` prop can also be generated dynamically. For example, to show a list of posts which have been passed to the page as a prop: +> [`encodeURIComponent`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) is used in the example to keep the path utf-8 compatible. + +Alternatively, using a URL Object: ```jsx -function Home({ posts }) { +import Link from 'next/link' + +function Posts({ posts }) { return (
      {posts.map((post) => (
    • - + {post.title}
    • @@ -100,8 +125,15 @@ function Home({ posts }) {
    ) } + +export default Posts ``` +Now, instead of using interpolation to create the path, we use a URL object in `href` where: + +- `pathname` is the name of the page in the `pages` directory. `/blog/[slug]` in this case. +- `query` is an object with the dynamic segment. `slug` in this case. + ## Injecting the router
    diff --git a/docs/routing/shallow-routing.md b/docs/routing/shallow-routing.md index 9aa21feae566b..caa9cf35138e1 100644 --- a/docs/routing/shallow-routing.md +++ b/docs/routing/shallow-routing.md @@ -38,14 +38,6 @@ function Page() { export default Page ``` -If you don't need to add the router object to the page, you can also use the [Router API](/docs/api-reference/next/router.md#router-api) directly, like so: - -```jsx -import Router from 'next/router' -// Inside your page -Router.push('/?counter=10', undefined, { shallow: true }) -``` - The URL will get updated to `/?counter=10`. and the page won't get replaced, only the state of the route is changed. You can also watch for URL changes via [`componentDidUpdate`](https://reactjs.org/docs/react-component.html#componentdidupdate) as shown below: @@ -65,7 +57,7 @@ componentDidUpdate(prevProps) { Shallow routing **only** works for same page URL changes. For example, let's assume we have another page called `pages/about.js`, and you run this: ```jsx -Router.push('/?counter=10', '/about?counter=10', { shallow: true }) +router.push('/?counter=10', '/about?counter=10', { shallow: true }) ``` Since that's a new page, it'll unload the current page, load the new one and wait for data fetching even though we asked to do shallow routing. diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000000000..6d723e3407742 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,350 @@ +--- +description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Jest, and React Testing Library. +--- + +# Testing + +
    + Examples + +
    + +Learn how to set up Next.js with three commonly used testing tools: [Cypress](https://www.cypress.io/blog/2021/04/06/cypress-component-testing-react/), [Jest](https://jestjs.io/docs/tutorial-react), and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). + +## Cypress + +Cypress is a test runner used for **End-to-End (E2E)** and **Integration Testing**. + +### Quickstart + +You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started. + +```bash +npx create-next-app --example with-cypress with-cypress-app +``` + +### Manual setup + +To get started with Cypress, install the `cypress` package: + +```bash +npm install --save-dev cypress +``` + +Add Cypress to the `package.json` scripts field: + +```json +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "cypress": "cypress open", +} +``` + +Run Cypress for the first time to generate examples that use their recommended folder structure: + +```bash +npm run cypress +``` + +You can look through the generated examples and the [Writing Your First Test](https://docs.cypress.io/guides/getting-started/writing-your-first-test) section of the Cypress Documentation to help you get familiar with Cypress. + +### Creating your first Cypress integration test + +Assuming the following two Next.js pages: + +```jsx +// pages/index.js +import Link from 'next/link' + +export default function Home() { + return ( + + ) +} +``` + +```jsx +// pages/about.js +export default function About() { + return ( +
    +

    About Page

    +
    + ) +} +``` + +Add a test to check your navigation is working correctly: + +```jsx +// cypress/integration/app.spec.js + +describe('Navigation', () => { + it('should navigate to the about page', () => { + // Start from the index page + cy.visit('http://localhost:3000/') + + // Find a link with an href attribute containing "about" and click it + cy.get('a[href*="about"]').click() + + // The new url should include "/about" + cy.url().should('include', '/about') + + // The new page should contain an h1 with "About page" + cy.get('h1').contains('About Page') + }) +}) +``` + +You can use `cy.visit("/")` instead of `cy.visit("http://localhost:3000/")` if you add `"baseUrl": "http://localhost:3000"` to the `cypress.json` configuration file. + +### Running your Cypress tests + +Since Cypress is testing a real Next.js application, it requires the Next.js server to be running prior to starting Cypress. We recommend running your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build` and `npm run start`, then run `npm run cypress` in another terminal window to start Cypress. + +> **Note:** Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test start http://localhost:3000 cypress"` to start the Next.js production server in conjuction with Cypress. Remember to rebuild your application after new changes. + +### Getting ready for Continuous Integration (CI) + +You will have noticed that running Cypress so far has opened an interactive browser which is not ideal for CI environments. You can also run Cypress headlessly using the `cypress run` command: + +```json +// package.json + +"scripts": { + //... + "cypress": "cypress open", + "cypress:headless": "cypress run", + "e2e": "start-server-and-test start http://localhost:3000 cypress", + "e2e:headless": "start-server-and-test start http://localhost:3000 cypress:headless" +} +``` + +You can learn more about Cypress and Continuous Integration from these resources: + +- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) +- [Cypress GitHub Actions Guide](https://on.cypress.io/github-actions) +- [Official Cypress Github Action](https://github.com/cypress-io/github-action) + +## Jest and React Testing Library + +Jest and React Testing Library are frequently used together for **Unit Testing**. + +### Quickstart + +You can use `create-next-app` with the [with-jest example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) to quickly get started with Jest and React Testing Library: + +```bash +npx create-next-app --example with-jest with-jest-app +``` + +### Manual setup + +To manually set up Jest and React Testing Library, install `jest` , `@testing-library/react`, `@testing-library/jest-dom` as well as some supporting packages: + +```bash +npm install --save-dev jest babel-jest @testing-library/react @testing-library/jest-dom identity-obj-proxy react-test-renderer +``` + +**Configuring Jest** + +Create a `jest.config.js` file in your project's root directory and add the following configuration options: + +```jsx +// jest.config.js + +module.exports = { + collectCoverageFrom: [ + '**/*.{js,jsx,ts,tsx}', + '!**/*.d.ts', + '!**/node_modules/**', + ], + moduleNameMapper: { + /* Handle CSS imports (with CSS modules) + https://jestjs.io/docs/webpack#mocking-css-modules */ + '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', + + // Handle CSS imports (without CSS modules) + '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', + + /* Handle image imports + https://jestjs.io/docs/webpack#handling-static-assets */ + '^.+\\.(jpg|jpeg|png|gif|webp|svg)$': '/__mocks__/fileMock.js', + }, + testPathIgnorePatterns: ['/node_modules/', '/.next/'], + testEnvironment: 'jsdom', + transform: { + /* Use babel-jest to transpile tests with the next/babel preset + https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object */ + '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], + }, + transformIgnorePatterns: [ + '/node_modules/', + '^.+\\.module\\.(css|sass|scss)$', + ], +} +``` + +You can learn more about each option above in the [Jest docs](https://jestjs.io/docs/configuration). + +**Handling stylesheets and image imports** + +These files aren't useful in tests but importing them may cause errors, so we will need to mock them. Create the mock files we referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: + +```json +// __mocks__/fileMock.js + +(module.exports = "test-file-stub") +``` + +```json +// __mocks__/styleMock.js + +module.exports = {}; +``` + +For more information on handling static assets, please refer to the [Jest Docs](https://jestjs.io/docs/webpack#handling-static-assets). + +**Extend Jest with custom matchers** + +`@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: + +```json +// jest.config.js + +setupFilesAfterEnv: ['/jest.setup.js'] +``` + +Then, inside `jest.setup.js`, add the following import: + +```jsx +// jest.setup.js + +import '@testing-library/jest-dom/extend-expect' +``` + +If you need to add more setup options before each test, it's common to add them to the `jest.setup.js` file above. + +**Absolute Imports and Module Path Aliases** + +If your project is using [Module Path Aliases](https://nextjs.org/docs/advanced-features/module-path-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: + +```json +// tsconfig.json or jsconfig.json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/components/*": ["components/*"] + } + } +} +``` + +```jsx +// jest.config.js +moduleNameMapper: { + '^@/components/(.*)$': '/components/$1', +} +``` + +**Add a test script to package.json** + +Add the Jest executable in watch mode to the `package.json` scripts: + +```jsx +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test": "jest --watch" +} +``` + +`jest --watch` will re-run tests when a file is changed. For more Jest CLI options, please refer to the [Jest Docs](https://jestjs.io/docs/cli#reference). + +**Create your first tests** + +Your project is now ready to run tests. Follow Jests convention by adding tests to the `__tests__` folder in your project's root directory. + +For example, we can add a test to check if the `` component successfully renders a heading: + +```jsx +// __tests__/index.test.jsx + +/** + * @jest-environment jsdom + */ + +import React from 'react' +import { render, screen } from '@testing-library/react' +import Home from '../pages/index' + +describe('Home', () => { + it('renders a heading', () => { + render() + + const heading = screen.getByRole('heading', { + name: /welcome to next\.js!/i, + }) + + expect(heading).toBeInTheDocument() + }) +}) +``` + +> **Note**: The `@jest-environment jsdom` comment above configures the testing environment as `jsdom` inside the test file because React Testing Library uses DOM elements like `document.body` which will not work in Jest's default `node` testing environment. Alternatively, you can also set the `jsdom` environment globally by adding the Jest configuration option: `"testEnvironment": "jsdom"` in `jest.config.js`. + +Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `` component: + +```jsx +// __tests__/snapshot.js +import React from 'react' +import renderer from 'react-test-renderer' +import Index from '../pages/index' + +it('renders homepage unchanged', () => { + const tree = renderer.create().toJSON() + expect(tree).toMatchSnapshot() +}) +``` + +> **Note**: Test files should not be included inside the pages directory because any files inside the pages directory are considered routes. + +**Running your test suite** + +Run `npm run jest` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. + +For further reading, you may find these resources helpful: + +- [Jest Docs](https://jestjs.io/docs/getting-started) +- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) +- [Testing Playground](https://testing-playground.com/) - use good testing practices to match elements. + +## Community Packages and Examples + +The Next.js community has created packages and articles you may find helpful: + +- [next-page-tester](https://github.com/toomuchdesign/next-page-tester) for DOM Integration Testing. +- [next-router-mock](https://github.com/scottrippey/next-router-mock) for Storybook. +- [Test Preview Vercel Deploys with Cypress](https://glebbahmutov.com/blog/develop-preview-test/) by Gleb Bahmutov. + +For more information on what to read next, we recommend: + + diff --git a/docs/upgrading.md b/docs/upgrading.md index 9b3b16fa9deb8..08a607ae83f4a 100644 --- a/docs/upgrading.md +++ b/docs/upgrading.md @@ -4,21 +4,180 @@ description: Learn how to upgrade Next.js. # Upgrade Guide -## Upgrading from version 8 to 9.0.x +## Upgrading from version 10 to 11 + +### Upgrade React version to latest + +Most applications already use the latest version of React, with Next.js 11 the minimum React version has been updated to 17.0.2. + +To upgrade you can run the following command: + +``` +npm install react@latest react-dom@latest +``` + +Or using `yarn`: + +``` +yarn add react@latest react-dom@latest +``` + +### Upgrade Next.js version to latest + +To upgrade you can run the following command in the terminal: + +``` +npm install next@latest +``` + +or + +``` +yarn add next@latest +``` + +### Webpack 5 + +Webpack 5 is now the default for all Next.js applications. If you did not have custom webpack configuration your application is already using webpack 5. If you do have custom webpack configuration you can refer to the [Next.js webpack 5 documentation](https://nextjs.org/docs/messages/webpack5) for upgrading guidance. + +### Cleaning the `distDir` is now a default + +The build output directory (defaults to `.next`) is now cleared by default except for the Next.js caches. You can refer to [the cleaning `distDir` RFC](https://github.com/vercel/next.js/discussions/6009) for more information. + +If your application was relying on this behavior previously you can disable the new default behavior by adding the `cleanDistDir: false` flag in `next.config.js`. + +### `PORT` is now supported for `next dev` and `next start` + +Next.js 11 supports the `PORT` environment variable to set the port the application has to run on. Using `-p`/`--port` is still recommended but if you were prohibited from using `-p` in any way you can now use `PORT` as an alternative: + +Example: + +``` +PORT=4000 next start +``` + +### `next.config.js` customization to import images + +Next.js 11 supports static image imports with `next/image`. This new feature relies on being able to process image imports. If you previously added the `next-images` or `next-optimized-images` packages you can either move to the new built-in support using `next/image` or disable the feature: + +```js +module.exports = { + images: { + disableStaticImages: true, + }, +} +``` + +### Remove `super.componentDidCatch()` from `pages/_app.js` + +The `next/app` component's `componentDidCatch` has been deprecated since Next.js 9 as it's no longer needed and has since been a no-op, in Next.js 11 it has been removed. + +If your `pages/_app.js` has a custom `componentDidCatch` method you can remove `super.componentDidCatch` as it is no longer needed. + +### Remove `Container` from `pages/_app.js` + +This export has been deprecated since Next.js 9 as it's no longer needed and has since been a no-op with a warning during development. In Next.js 11 it has been removed. + +If your `pages/_app.js` imports `Container` from `next/app` you can remove `Container` as it has been removed. Learn more in [the documentation](https://nextjs.org/docs/messages/app-container-deprecated). + +### Remove `props.url` usage from page components + +This property has been deprecated since Next.js 4 and has since shown a warning during development. With the introduction of `getStaticProps` / `getServerSideProps` these methods already disallowed usage of `props.url`. In Next.js 11 it has been removed completely. + +You can learn more in [the documentation](https://nextjs.org/docs/messages/url-deprecated). + +### Remove `unsized` property on `next/image` + +The `unsized` property on `next/image` was deprecated in Next.js 10.0.1. You can use `layout="fill"` instead. In Next.js 11 `unsized` was removed. + +### Remove `modules` property on `next/dynamic` + +The `modules` and `render` option for `next/dynamic` have been deprecated since Next.js 9.5 showing a warning that it has been deprecated. This was done in order to make `next/dynamic` close to `React.lazy` in API surface. In Next.js 11 the `modules` and `render` options have been removed. + +This option hasn't been mentioned in the documentation since Next.js 8 so it's less likely that your application is using it. + +If you application does use `modules` and `render` you can refer to [the documentation](https://nextjs.org/docs/messages/next-dynamic-modules). + +### Remove `Head.rewind` + +`Head.rewind` has been a no-op since Next.js 9.5, in Next.js 11 it was removed. You can safely remove your usage of `Head.rewind`. + +### Moment.js locales excluded by default + +Moment.js includes translations for a lot of locales by default. Next.js now automatically excludes these locales by default to optimize bundle size for applications using Moment.js. + +To load a specific locale use this snippet: + +```js +import moment from 'moment' +import 'moment/locale/ja' + +moment.locale('ja') +``` + +You can opt-out of this new default by adding `excludeDefaultMomentLocales: false` to `next.config.js` if you do not want the new behavior, do note it's highly recommended to not disable this new optimization as it significantly reduces the size of Moment.js. + +### Update usage of `router.events` + +In case you're accessing `router.events` during rendering, in Next.js 11 `router.events` is no longer provided during pre-rendering. Ensure you're accessing `router.events` in `useEffect`: + +```js +useEffect(() => { + const handleRouteChange = (url, { shallow }) => { + console.log( + `App is changing to ${url} ${ + shallow ? 'with' : 'without' + } shallow routing` + ) + } + + router.events.on('routeChangeStart', handleRouteChange) + + // If the component is unmounted, unsubscribe + // from the event with the `off` method: + return () => { + router.events.off('routeChangeStart', handleRouteChange) + } +}, [router]) +``` + +If your application uses `router.router.events` which was an internal property that was not public please make sure to use `router.events` as well. + +## React 16 to 17 + +React 17 introduced a new [JSX Transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) that brings a long-time Next.js feature to the wider React ecosystem: Not having to `import React from 'react'` when using JSX. When using React 17 Next.js will automatically use the new transform. This transform does not make the `React` variable global, which was an unintended side-effect of the previous Next.js implementation. A [codemod is available](/docs/advanced-features/codemods.md#add-missing-react-import) to automatically fix cases where you accidentally used `React` without importing it. + +## Upgrading from version 9 to 10 + +There were no breaking changes between version 9 and 10. + +To upgrade run the following command: + +``` +npm install next@10 +``` + +Or using `yarn`: + +``` +yarn add next@10 +``` + +## Upgrading from version 8 to 9 ### Preamble #### Production Deployment on Vercel -If you previously configured `routes` in your `now.json` file for dynamic routes, these rules can be removed when leveraging Next.js 9's new [Dynamic Routing feature](https://nextjs.org/docs/routing/dynamic-routes). +If you previously configured `routes` in your `vercel.json` file for dynamic routes, these rules can be removed when leveraging Next.js 9's new [Dynamic Routing feature](/docs/routing/dynamic-routes.md). -Next.js 9's dynamic routes are **automatically configured on [Now](https://vercel.com/now)** and do not require any `now.json` customization. +Next.js 9's dynamic routes are **automatically configured on [Vercel](https://vercel.com/)** and do not require any `vercel.json` customization. -You can read more about [Dynamic Routing here](https://nextjs.org/docs/routing/dynamic-routes). +You can read more about [Dynamic Routing here](/docs/routing/dynamic-routes.md). #### Check your Custom (`pages/_app.js`) -If you previously copied the [Custom ``](https://nextjs.org/docs#custom-app) example, you may be able to remove your `getInitialProps`. +If you previously copied the [Custom ``](/docs/advanced-features/custom-app.md) example, you may be able to remove your `getInitialProps`. Removing `getInitialProps` from `pages/_app.js` (when possible) is important to leverage new Next.js features! @@ -75,7 +234,7 @@ import { AppContext, AppInitialProps } from 'next/app' import { DocumentContext, DocumentInitialProps } from 'next/document' ``` -#### The `config` key is now a special export on a page +#### The `config` key is now an export on a page You may no longer export a custom variable named `config` from a page (i.e. `export { config }` / `export const config ...`). This exported variable is now used to specify page-level Next.js configuration like Opt-in AMP and API Route features. @@ -104,7 +263,7 @@ Next.js now has the concept of page-level configuration, so the `withAmp` higher This change can be **automatically migrated by running the following commands in the root of your Next.js project:** ```bash -curl -L https://github.com/zeit/next-codemod/archive/master.tar.gz | tar -xz --strip=2 next-codemod-master/transforms/withamp-to-config.js npx jscodeshift -t ./withamp-to-config.js pages/**/*.js +curl -L https://github.com/vercel/next-codemod/archive/master.tar.gz | tar -xz --strip=2 next-codemod-master/transforms/withamp-to-config.js npx jscodeshift -t ./withamp-to-config.js pages/**/*.js ``` To perform this migration by hand, or view what the codemod will produce, see below: @@ -146,7 +305,7 @@ You can revert to the previous behavior by creating a `next.config.js` with the ```js // next.config.js module.exports = { - exportTrailingSlash: true, + trailingSlash: true, } ``` diff --git a/errors/api-routes-body-size-limit.md b/errors/api-routes-body-size-limit.md new file mode 100644 index 0000000000000..5697768c4a089 --- /dev/null +++ b/errors/api-routes-body-size-limit.md @@ -0,0 +1,13 @@ +# API Routes Body Size Limited to 4MB + +#### Why This Error Occurred + +API Routes are meant to respond quickly and are not intended to support responding with large amounts of data. The maximum size of responses is 4 MB. + +#### Possible Ways to Fix It + +Limit your API Route responses to less than 4 MB. If you need to support sending large files to the client, you should consider using a dedicated media host for those assets. See link below for suggestions. + +### Useful Links + +[Tips to avoid the 5 MB limit](https://vercel.com/support/articles/how-to-bypass-vercel-5mb-body-size-limit-serverless-functions) diff --git a/errors/api-routes-static-export.md b/errors/api-routes-static-export.md index 2748d46e564c1..8b2388ef561fd 100644 --- a/errors/api-routes-static-export.md +++ b/errors/api-routes-static-export.md @@ -4,7 +4,7 @@ An `exportPathMap` path was matched to an API route. Statically exporting a Next.js application via `next export` disables API routes. -This command is meant for static-only hosts, and is not necessary to make your application static. Pages in your application without server-side data dependencies will be automatically statically exported by `next build`, including pages powered by `getStaticProps` +This command is meant for a static-only setup, and is not necessary to make your application static. Pages in your application without server-side data dependencies will be automatically statically exported by `next build`, including pages powered by `getStaticProps` #### Possible Ways to Fix It @@ -12,4 +12,4 @@ Use `next build` with platforms that don't require `next export` like https://ve ### Useful Links -- [Static HTML export](https://nextjs.org/docs#static-html-export) +- [Static HTML export](https://nextjs.org/docs/advanced-features/static-html-export) diff --git a/errors/build-dir-not-writeable.md b/errors/build-dir-not-writeable.md index 5b8186ff5ed5e..98b6f04a20599 100644 --- a/errors/build-dir-not-writeable.md +++ b/errors/build-dir-not-writeable.md @@ -2,7 +2,7 @@ #### Why This Error Occurred -The filesystem does not allow writing to the specified directory. A common cause for this error is starting a [custom server](https://nextjs.org/docs#custom-server-and-routing) in development mode on a production server, for example, [Vercel](https://vercel.com) which [doesn't allow you to write to the filesystem after your app is built](https://vercel.com/docs/deployment-types/node#file-system-specifications). +The filesystem does not allow writing to the specified directory. A common cause for this error is starting a [custom server](https://nextjs.org/docs/advanced-features/custom-server) in development mode on a production server, for example, [Vercel](https://vercel.com) which doesn't allow you to write to the filesystem after your app is built. #### Possible Ways to Fix It @@ -27,4 +27,4 @@ const app = next({ dev }) ### Useful Links -- [Custom Server documentation + examples](https://nextjs.org/docs#custom-server-and-routing) +- [Custom Server documentation + examples](https://nextjs.org/docs/advanced-features/custom-server) diff --git a/errors/can-not-output-to-public.md b/errors/can-not-output-to-public.md index 5a266aeb8fc4c..1423bd329c8dd 100644 --- a/errors/can-not-output-to-public.md +++ b/errors/can-not-output-to-public.md @@ -4,7 +4,7 @@ Either you set `distDir` to `public` in your `next.config.js` or during `next export` you tried to export to the `public` directory. -This is not allowed due to `public` being a special folder in Next.js used to serve static assets. +This is not allowed because `public` is used by Next.js to serve static assets. #### Possible Ways to Fix It @@ -12,4 +12,4 @@ Use a different `distDir` or export to a different folder. ### Useful Links -- [Static file serving docs](https://nextjs.org/docs#static-file-serving-eg-images) +- [Static file serving docs](https://nextjs.org/docs/basic-features/static-file-serving) diff --git a/errors/can-not-output-to-static.md b/errors/can-not-output-to-static.md new file mode 100644 index 0000000000000..750335e63eef5 --- /dev/null +++ b/errors/can-not-output-to-static.md @@ -0,0 +1,15 @@ +# Cannot output to /static + +#### Why This Error Occurred + +Either you set `distDir` to `static` in your `next.config.js` or during `next export` you tried to export to the `static` directory. + +This is not allowed because `static` is used by Next.js to serve static assets. + +#### Possible Ways to Fix It + +Use a different `distDir` or export to a different folder. + +### Useful Links + +- [Static file serving docs](https://nextjs.org/docs/basic-features/static-file-serving) diff --git a/errors/client-side-exception-occurred.md b/errors/client-side-exception-occurred.md new file mode 100644 index 0000000000000..06c1dcf5148d1 --- /dev/null +++ b/errors/client-side-exception-occurred.md @@ -0,0 +1,14 @@ +# Client-side Exception Occurred + +#### Why This Error Occurred + +In your production application a client-side error occurred that was not caught by an error boundary. Additional information should be visible in the console tab of your browser. + +#### Possible Ways to Fix It + +Add error boundaries in your React tree to gracefully handle client-side errors and render a fallback view when they occur. + +### Useful Links + +- [Error Boundaries Documentation](https://reactjs.org/docs/error-boundaries.html) +- [Custom Error Page Documentation](https://nextjs.org/docs/advanced-features/custom-error-page#more-advanced-error-page-customizing) diff --git a/errors/conflicting-public-file-page.md b/errors/conflicting-public-file-page.md index 3000fdd75fab7..397f34c98360e 100644 --- a/errors/conflicting-public-file-page.md +++ b/errors/conflicting-public-file-page.md @@ -28,4 +28,4 @@ pages/ ### Useful Links -- [Static file serving docs](https://nextjs.org/docs#static-file-serving-eg-images) +- [Static file serving docs](https://nextjs.org/docs/basic-features/static-file-serving) diff --git a/errors/conflicting-ssg-paths.md b/errors/conflicting-ssg-paths.md new file mode 100644 index 0000000000000..eb3e71a965bd9 --- /dev/null +++ b/errors/conflicting-ssg-paths.md @@ -0,0 +1,68 @@ +# Conflicting SSG Paths + +#### Why This Error Occurred + +In your `getStaticPaths` function for one of your pages you returned conflicting paths. All page paths must be unique and duplicates are not allowed. + +#### Possible Ways to Fix It + +Remove any conflicting paths shown in the error message and only return them from one `getStaticPaths`. + +Example conflicting paths: + +```jsx +// pages/hello/world.js +export default function Hello() { + return 'hello world!' +} + +// pages/[...catchAll].js +export const getStaticProps = () => ({ props: {} }) + +export const getStaticPaths = () => ({ + paths: [ + // this conflicts with the /hello/world.js page, remove to resolve error + '/hello/world', + '/another', + ], + fallback: false, +}) + +export default function CatchAll() { + return 'Catch-all page' +} +``` + +Example conflicting paths: + +```jsx +// pages/blog/[slug].js +export const getStaticPaths = () => ({ + paths: ['/blog/conflicting', '/blog/another'], + fallback: false, +}) + +export default function Blog() { + return 'Blog!' +} + +// pages/[...catchAll].js +export const getStaticProps = () => ({ props: {} }) + +export const getStaticPaths = () => ({ + paths: [ + // this conflicts with the /blog/conflicting path above, remove to resolve error + '/blog/conflicting', + '/another', + ], + fallback: false, +}) + +export default function CatchAll() { + return 'Catch-all page' +} +``` + +### Useful Links + +- [`getStaticPaths` Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation) diff --git a/errors/css-global.md b/errors/css-global.md index 22c4e87c5e78b..0f2ff4fd84319 100644 --- a/errors/css-global.md +++ b/errors/css-global.md @@ -8,10 +8,27 @@ Global CSS cannot be used in files other than your [Custom ``](https://next #### Possible Ways to Fix It -Relocate all Global CSS imports to your [`pages/_app.js` file](https://nextjs.org/docs/advanced-features/custom-app). +To avoid conflicts, relocate all first-party Global CSS imports to your [`pages/_app.js` file](https://nextjs.org/docs/advanced-features/custom-app). + +Or, [update your component to use local CSS (Component-Level CSS) via CSS Modules](https://nextjs.org/docs/basic-features/built-in-css-support#adding-component-level-css). This is the preferred approach. #### Example +Consider the stylesheet named [`styles.css`](https://nextjs.org/docs/basic-features/built-in-css-support#adding-a-global-stylesheet) + +```jsx +//styles.css +body { + font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica', + 'Arial', sans-serif; + padding: 20px 20px 60px; + max-width: 680px; + margin: 0 auto; +} +``` + +Create a [`pages/_app.js` file](https://nextjs.org/docs/advanced-features/custom-app) if not already present. Then [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) the [`styles.css` file](https://nextjs.org/docs/basic-features/built-in-css-support#adding-a-global-stylesheet). + ```jsx // pages/_app.js import '../styles.css' diff --git a/errors/duplicate-sass.md b/errors/duplicate-sass.md new file mode 100644 index 0000000000000..aa4e6e9b8b39d --- /dev/null +++ b/errors/duplicate-sass.md @@ -0,0 +1,33 @@ +# Duplicate Sass Dependencies + +#### Why This Error Occurred + +Your project has a direct dependency on both `sass` and `node-sass`, two +different package that both compile Sass files! + +Next.js will only use one of these, so it is suggested you remove one or the +other. + +#### Possible Ways to Fix It + +The `sass` package is a modern implementation of Sass in JavaScript that +supports all the new features and does not require any native dependencies. + +Since `sass` is now the canonical implementation, we suggest removing the older +`node-sass` package, which should speed up your builds and project install time. + +**Via npm** + +```bash +npm uninstall node-sass +``` + +**Via Yarn** + +```bash +yarn remove node-sass +``` + +### Useful Links + +- [`sass` package documentation](https://github.com/sass/dart-sass) diff --git a/errors/export-all-in-page.md b/errors/export-all-in-page.md new file mode 100644 index 0000000000000..c2fe449a8a0bd --- /dev/null +++ b/errors/export-all-in-page.md @@ -0,0 +1,54 @@ +# Re-exporting all exports from a page is disallowed + +#### Why This Error Occurred + +The following export can potentially break Next.js' compilation of pages: + +```ts +export * from '...' +``` + +This is because Node.js code may be leaked to the browser build, causing an error. For example, the following two pages: + +```ts +// pages/one.js +import fs from 'fs' + +export default function A() { + return
    +} + +export function getStaticProps() { + fs + return { props: {} } +} +``` + +```ts +// pages/two.js +export * from './one' +``` + +Would cause the following error: + +``` +Module not found: Can't resolve 'fs' in './pages/two.js' +``` + +#### Possible Ways to Fix It + +Update your page to re-export the default component only: + +```ts +export { default } from './other-page' +``` + +If the other page uses `getServerSideProps` or `getStaticProps`, you can re-export those individually too: + +```ts +export { default, getServerSideProps } from './other-page' +// or +export { default, getStaticProps } from './other-page' +// or +export { default, getStaticProps, getStaticPaths } from './other-page/[dynamic]' +``` diff --git a/errors/export-image-api.md b/errors/export-image-api.md new file mode 100644 index 0000000000000..dae959c90dcaf --- /dev/null +++ b/errors/export-image-api.md @@ -0,0 +1,24 @@ +# `next export` with Image API + +#### Why This Error Occurred + +You are attempting to run `next export` while importing the `next/image` component configured using the default `loader`. + +However, the default `loader` relies on the Image Optimization API which is not available for exported applications. + +This is because Next.js optimizes images on-demand, as users request them (not at build time). + +#### Possible Ways to Fix It + +- Use `next start` to run a server, which includes the Image Optimization API. +- Use any provider which supports Image Optimization (like [Vercel](https://vercel.com/docs/next.js/image-optimization)). +- Configure a third-party [loader](https://nextjs.org/docs/basic-features/image-optimization#loader) in `next.config.js`. +- Use the [`loader`](https://nextjs.org/docs/api-reference/next/image#loader) prop for `next/image`. + +### Useful Links + +- [Deployment Documentation](https://nextjs.org/docs/deployment#vercel-recommended) +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) +- [`next export` Documentation](https://nextjs.org/docs/advanced-features/static-html-export) +- [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) +- [Vercel Documentation](https://vercel.com/docs/next.js/image-optimization) diff --git a/errors/export-no-custom-routes.md b/errors/export-no-custom-routes.md new file mode 100644 index 0000000000000..9a18c5747e24c --- /dev/null +++ b/errors/export-no-custom-routes.md @@ -0,0 +1,19 @@ +# `next export` No Custom Routes + +#### Why This Error Occurred + +In your `next.config.js` `rewrites`, `redirects`, or `headers` were defined while `next export` was being run outside of a platform that supports them. + +These configs do not apply when exporting your Next.js application manually. + +#### Possible Ways to Fix It + +Disable the `rewrites`, `redirects`, and `headers` from your `next.config.js` when using `next export` to deploy your application or deploy your application using [a method](https://nextjs.org/docs/deployment#vercel-recommended) that supports these configs. + +### Useful Links + +- [Deployment Documentation](https://nextjs.org/docs/deployment#vercel-recommended) +- [`next export` Documentation](https://nextjs.org/docs/advanced-features/static-html-export) +- [Rewrites Documentation](https://nextjs.org/docs/api-reference/next.config.js/rewrites) +- [Redirects Documentation](https://nextjs.org/docs/api-reference/next.config.js/redirects) +- [Headers Documentation](https://nextjs.org/docs/api-reference/next.config.js/headers) diff --git a/errors/export-path-mismatch.md b/errors/export-path-mismatch.md index bbd0abdbca5bb..1900a08b74fb4 100644 --- a/errors/export-path-mismatch.md +++ b/errors/export-path-mismatch.md @@ -23,4 +23,4 @@ module.exports = { ### Useful Links -- [exportPathMap](https://nextjs.org/docs#usage) documentation +- [exportPathMap](https://nextjs.org/docs/api-reference/next.config.js/exportPathMap) documentation diff --git a/errors/future-webpack5-moved-to-webpack5.md b/errors/future-webpack5-moved-to-webpack5.md new file mode 100644 index 0000000000000..27aadf72a2a85 --- /dev/null +++ b/errors/future-webpack5-moved-to-webpack5.md @@ -0,0 +1,33 @@ +# `future.webpack5` has been moved to `webpack5` + +#### Why This Error Occurred + +The `future.webpack5` option has been moved to `webpack5` in `next.config.js`. + +#### Possible Ways to Fix It + +If you had the value `true` you can remove the option as webpack 5 is now the default for all Next.js apps unless opted out. + +If you had the value `false` you can update `next.config.js`: + +Change `future.webpack5` to `webpack5`. + +Current `next.config.js`: + +```js +// next.config.js +module.exports = { + future: { + webpack5: false, + }, +} +``` + +Updated `next.config.js`: + +```js +// next.config.js +module.exports = { + webpack5: false, +} +``` diff --git a/errors/get-initial-props-as-an-instance-method.md b/errors/get-initial-props-as-an-instance-method.md index 115882f4b9fc1..26218c66e28eb 100644 --- a/errors/get-initial-props-as-an-instance-method.md +++ b/errors/get-initial-props-as-an-instance-method.md @@ -36,4 +36,4 @@ export default YourEntryComponent ### Useful Links -- [Fetching data and component lifecycle](https://nextjs.org/docs#fetching-data-and-component-lifecycle) +- [Fetching data and component lifecycle](https://nextjs.org/docs/api-reference/data-fetching/getInitialProps) diff --git a/errors/google-font-display.md b/errors/google-font-display.md new file mode 100644 index 0000000000000..61328e8976282 --- /dev/null +++ b/errors/google-font-display.md @@ -0,0 +1,36 @@ +# Google Font Display + +### Why This Error Occurred + +For a Google Font, the `display` descriptor was either not assigned or set to `auto`, `fallback`, or `block`. + +### Possible Ways to Fix It + +For most cases, the best font display strategy for custom fonts is `optional`. + +```jsx +import Head from 'next/head' + +export default function IndexPage() { + return ( +
    + + + +
    + ) +} +``` + +Specifying `display=optional` minimizes the risk of invisible text or layout shift. If swapping to the custom font after it has loaded is important to you, then use `display=swap` instead. + +### When Not To Use It + +If you want to specifically display a font using a `block` or `fallback` strategy, then you can disable this rule. + +### Useful Links + +- [Font-display](https://font-display.glitch.me/) diff --git a/errors/google-font-preconnect.md b/errors/google-font-preconnect.md new file mode 100644 index 0000000000000..de629ac4e1bf3 --- /dev/null +++ b/errors/google-font-preconnect.md @@ -0,0 +1,17 @@ +# Google Font Preconnect + +### Why This Error Occurred + +A preconnect resource hint was not used with a request to the Google Fonts domain. Adding `preconnect` is recommended to initiate an early connection to the origin. + +### Possible Ways to Fix It + +Add `rel="preconnect"` to the Google Font domain `` tag: + +```jsx + +``` + +### Useful Links + +- [Preconnect to required origins](https://web.dev/uses-rel-preconnect/) diff --git a/errors/gsp-redirect-during-prerender.md b/errors/gsp-redirect-during-prerender.md new file mode 100644 index 0000000000000..964d7bf1db1ff --- /dev/null +++ b/errors/gsp-redirect-during-prerender.md @@ -0,0 +1,13 @@ +# Redirect During getStaticProps Prerendering + +#### Why This Error Occurred + +The `redirect` value was returned from `getStaticProps` during prerendering which is invalid. + +#### Possible Ways to Fix It + +Remove any paths that result in a redirect from being prerendered in `getStaticPaths` and enable `fallback: true` to handle redirecting for these pages. + +### Useful Links + +- [Data Fetching Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) diff --git a/errors/gssp-component-member.md b/errors/gssp-component-member.md index 802416079ffe3..4f734fffe7e1a 100644 --- a/errors/gssp-component-member.md +++ b/errors/gssp-component-member.md @@ -1,4 +1,4 @@ -# getStaticProps/getServerProps can not be attached to the page component +# getStaticProps/getServerSideProps can not be attached to the page component #### Why This Error Occurred diff --git a/errors/gssp-export.md b/errors/gssp-export.md index b9908fadc58d0..62909cabf4e39 100644 --- a/errors/gssp-export.md +++ b/errors/gssp-export.md @@ -2,13 +2,37 @@ #### Why This Error Occurred -You attempted to export a page with `getServerSideProps` which is not allowed. `getServerSideProps` is meant for requesting up to date information on every request which means exporting it defeats the purpose of the method. +You attempted to statically export your application via `next export`, however, one or more of your pages uses `getServerSideProps`. + +The `getServerSideProps` lifecycle is not compatible with `next export`, so you'll need to use `next start` or a [serverless deployment](https://vercel.com). #### Possible Ways to Fix It -If you would like the page be static you can leverage the `getStaticProps` method instead. +1. If you'd like to keep your application static, you can use `getStaticProps` instead of `getServerSideProps`. + +2. If you want to use server-side rendering, update your build command and remove `next export`. For example, in your `package.json`: + + ```diff + diff --git a/bla.json b/bla.json + index b84aa66c4..149e67565 100644 + --- a/bla.json + +++ b/bla.json + @@ -1,7 +1,7 @@ + { + "scripts": { + "dev": "next dev", + - "build": "next build && next export", + + "build": "next build", + "start": "next start" + } + } + ``` + +> **Note**: Removing `next export` does not mean your entire application is no longer static. +> Pages that use `getStaticProps` or [no lifecycle](https://nextjs.org/docs/advanced-features/automatic-static-optimization) **will still be static**! ### Useful Links +- [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization) - [`getStaticProps` documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) - [`exportPathMap` documentation](https://nextjs.org/docs/api-reference/next.config.js/exportPathMap) diff --git a/errors/gssp-mixed-not-found-redirect.md b/errors/gssp-mixed-not-found-redirect.md new file mode 100644 index 0000000000000..5f592ce610c6c --- /dev/null +++ b/errors/gssp-mixed-not-found-redirect.md @@ -0,0 +1,16 @@ +# Mixed `notFound` and `redirect` + +#### Why This Error Occurred + +In one of your page's `getStaticProps` or `getServerSideProps` `notFound` and `redirect` values were both returned. + +These values can not both be returned at the same time and one or the other needs to be returned at a time. + +#### Possible Ways to Fix It + +Make sure only `notFound` **or** `redirect` is being returned on your page's `getStaticProps` or `getServerSideProps` + +### Useful Links + +- [`getStaticProps` Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) +- [`getServerSideProps` Documentation](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) diff --git a/errors/head-build-id.md b/errors/head-build-id.md index f242ae413acf9..58618aaf55676 100644 --- a/errors/head-build-id.md +++ b/errors/head-build-id.md @@ -6,5 +6,5 @@ The deployment was generated incorrectly or the server was overloaded at the tim #### Possible Ways to Fix It -Please make sure you are using the latest version of the `@now/next` builder in your `now.json`. +Please make sure you are using the latest version of the `@vercel/next` builder in your `vercel.json`. If this error persists, please file a bug report. diff --git a/errors/href-interpolation-failed.md b/errors/href-interpolation-failed.md new file mode 100644 index 0000000000000..a3526207cba52 --- /dev/null +++ b/errors/href-interpolation-failed.md @@ -0,0 +1,54 @@ +# `href` Interpolation Failed + +#### Why This Error Occurred + +Somewhere you are utilizing the `next/link` component, `Router#push`, or `Router#replace` with `href` interpolation to build dynamic routes but did not provide all of the needed dynamic route params to properly interpolate the `href`. + +Note: this error will only show when the `next/link` component is clicked not when only rendered. + +**Invalid `href` interpolation** + +```jsx +import Link from 'next/link' + +export default function BlogLink() { + return ( + + Invalid link + + ) +} +``` + +**Valid `href` interpolation** + +```jsx +import Link from 'next/link' + +export default function BlogLink() { + return ( + + Valid link + + ) +} +``` + +#### Possible Ways to Fix It + +Look for any usage of the `next/link` component, `Router#push`, or `Router#replace` and make sure that the provided `href` has all needed dynamic params in the query. + +### Useful Links + +- [Routing section in Documentation](https://nextjs.org/docs/routing/introduction) +- [Dynamic routing section in Documentation](https://nextjs.org/docs/routing/dynamic-routes) diff --git a/errors/import-esm-externals.md b/errors/import-esm-externals.md new file mode 100644 index 0000000000000..ce2113d541e50 --- /dev/null +++ b/errors/import-esm-externals.md @@ -0,0 +1,19 @@ +# ESM packages need to be imported + +#### Why This Error Occurred + +Packages in node_modules that are published as EcmaScript Module, need to be `import`ed via `import ... from 'package'` or `import('package')`. + +You get this error when using a different way to reference the package, e. g. `require()`. + +#### Possible Ways to Fix It + +1. Use `import` or `import()` to reference the package instead. (Recommended) + +2. If you are already using `import`, make sure that this is not changed by a transpiler, e. g. TypeScript or Babel. + +3. Switch to loose mode (`experimental.esmExternals: 'loose'`), which tries to automatically correct this error. + +### Useful Links + +- [Node.js ESM require docs](https://nodejs.org/dist/latest-v16.x/docs/api/esm.html#esm_require) diff --git a/errors/improper-devtool.md b/errors/improper-devtool.md new file mode 100644 index 0000000000000..a026371264fcf --- /dev/null +++ b/errors/improper-devtool.md @@ -0,0 +1,20 @@ +# Improper webpack `devtool` used in development mode + +#### Why This Error Occurred + +Next.js chooses the most optimal `devtool` for use with webpack. Changing the `devtool` in development mode will cause severe performance regressions with your application. + +#### Possible Ways to Fix It + +Please remove the custom `devtool` override or only apply it to production builds in your `next.config.js`. + +```js +module.exports = { + webpack: (config, options) => { + if (!options.dev) { + config.devtool = options.isServer ? false : 'your-custom-devtool' + } + return config + }, +} +``` diff --git a/errors/incompatible-href-as.md b/errors/incompatible-href-as.md index 550fea5a21bbc..f6677e90b6e78 100644 --- a/errors/incompatible-href-as.md +++ b/errors/incompatible-href-as.md @@ -11,13 +11,15 @@ Note: this error will only show when the `next/link` component is clicked not wh ```jsx import Link from 'next/link' -export default () => ( - <> - - Invalid link - - -) +export default function Page(props) { + return ( + <> + + Invalid link + + + ) +} ``` **Compatible `href` and `as`** @@ -25,13 +27,15 @@ export default () => ( ```jsx import Link from 'next/link' -export default () => ( - <> - - Valid link - - -) +export default function Page(props) { + return ( + <> + + Valid link + + + ) +} ``` #### Possible Ways to Fix It @@ -40,5 +44,5 @@ Look for any usage of the `next/link` component, `Router#push`, or `Router#repla ### Useful Links -- [Routing section in Documentation](https://nextjs.org/docs#routing) -- [Dynamic routing section in Documentation](https://nextjs.org/docs#dynamic-routing) +- [Routing section in Documentation](https://nextjs.org/docs/routing/introduction) +- [Dynamic routing section in Documentation](https://nextjs.org/docs/routing/dynamic-routes) diff --git a/errors/inline-script-id.md b/errors/inline-script-id.md new file mode 100644 index 0000000000000..fe7f1250be733 --- /dev/null +++ b/errors/inline-script-id.md @@ -0,0 +1,26 @@ +# next/script components with inline content require an `id` attribute + +## Why This Error Occurred + +`next/script` components with inline content require an `id` attribute to be defined to track and optimize the script. + +## Possible Ways to Fix It + +Add an `id` attribute to the `next/script` component. + +```jsx +import Script from 'next/script' + +export default function App({ Component, pageProps }) { + return ( + <> + + + + ) +} +``` + +## Useful links + +- [Docs for Next.js Script component](https://nextjs.org/docs/basic-features/script) diff --git a/errors/install-sharp.md b/errors/install-sharp.md new file mode 100644 index 0000000000000..5c35f95b7357d --- /dev/null +++ b/errors/install-sharp.md @@ -0,0 +1,25 @@ +# Install `sharp` to Use Built-In Image Optimization + +#### Why This Error Occurred + +Using Next.js' built-in [Image Optimization](https://nextjs.org/docs/basic-features/image-optimization) requires [sharp](https://www.npmjs.com/package/sharp) as a dependency. + +You are seeing this error because your OS was unable to [install sharp](https://sharp.pixelplumbing.com/install) properly, either using pre-built binaries or building from source. + +#### Possible Ways to Fix It + +Option 1: Use a different version of Node.js and try to install `sharp` again. + +```bash +npm i sharp +# or +yarn add sharp +``` + +Option 2: If using macOS, ensure XCode Build Tools are installed and try to install `sharp` again. + +For example, see [macOS Catalina instructions](https://github.com/nodejs/node-gyp/blob/66c0f0446749caa591ad841cd029b6d5b5c8da42/macOS_Catalina.md). + +Option 3: Use a different OS and try to install `sharp` again. + +For example, if you're using Windows, try using [WSL](https://docs.microsoft.com/en-us/windows/wsl/about) (Windows Subsystem for Linux). diff --git a/errors/invalid-api-status-body.md b/errors/invalid-api-status-body.md new file mode 100644 index 0000000000000..b0d5fd60fd99d --- /dev/null +++ b/errors/invalid-api-status-body.md @@ -0,0 +1,32 @@ +Invalid API Route Status/Body Response + +#### Why This Error Occurred + +In one of your API routes a 204 or 304 status code was used as well as sending a response body. + +This is invalid as a 204 or 304 status code dictates no response body should be present. + +#### Possible Ways to Fix It + +Send an empty body when using a 204 or 304 status code or use a different status code while sending a response body. + +Before + +```js +export default function handler(req, res) { + res.status(204).send('invalid body') +} +``` + +After + +```js +export default function handler(req, res) { + res.status(204).send() +} +``` + +### Useful Links + +- [204 status code documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204) +- [304 status code documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304) diff --git a/errors/invalid-dynamic-suspense.md b/errors/invalid-dynamic-suspense.md new file mode 100644 index 0000000000000..11e4d6134d732 --- /dev/null +++ b/errors/invalid-dynamic-suspense.md @@ -0,0 +1,13 @@ +# Invalid Usage of `suspense` Option of `next/dynamic` + +#### Why This Error Occurred + +`` is not allowed under legacy render mode when using React older than v18. + +#### Possible Ways to Fix It + +Remove `suspense: true` option in `next/dynamic` usages. + +### Useful Links + +- [Dynamic Import Suspense Usage](https://nextjs.org/docs/advanced-features/dynamic-import#with-suspense) diff --git a/errors/invalid-external-rewrite.md b/errors/invalid-external-rewrite.md new file mode 100644 index 0000000000000..89d114af835bf --- /dev/null +++ b/errors/invalid-external-rewrite.md @@ -0,0 +1,13 @@ +# Invalid External Rewrite + +#### Why This Error Occurred + +A rewrite was defined with both `basePath: false` and an internal `destination`. Rewrites that capture urls outside of the `basePath` must route externally, as they are intended for proxying in the case of incremental adoption of Next.js in a project. + +#### Possible Ways to Fix It + +Look for any rewrite where `basePath` is `false` and make sure its `destination` starts with `http://` or `https://`. + +### Useful Links + +- [Rewrites section in Documentation](https://nextjs.org/docs/api-reference/next.config.js/rewrites) diff --git a/errors/invalid-getstaticprops-value.md b/errors/invalid-getstaticprops-value.md index ccbfd7232d077..16fcf6a9df158 100644 --- a/errors/invalid-getstaticprops-value.md +++ b/errors/invalid-getstaticprops-value.md @@ -12,7 +12,7 @@ Make sure to return the following shape from `getStaticProps`: export async function getStaticProps(ctx: { params?: ParsedUrlQuery preview?: boolean - previewData?: any + previewData?: PreviewData }) { return { props: { [key: string]: any } diff --git a/errors/invalid-href-passed.md b/errors/invalid-href-passed.md index d01d327d923a1..9d84c9da006d0 100644 --- a/errors/invalid-href-passed.md +++ b/errors/invalid-href-passed.md @@ -7,8 +7,7 @@ When using any of these, it is expected they are only used for internal navigati Either you passed a non-internal `href` to a `next/link` component or you called `Router#push` or `Router#replace` with one. -Invalid `href`s include external sites (https://google.com) and `mailto:` links. In the past, usage of these invalid `href`s could have gone unnoticed but since they can cause unexpected behavior. -We now show a warning in development for them. +Invalid `href`s include external sites (https://google.com) and `mailto:` links. In the past, usage of these invalid `href`s could have gone unnoticed, but since they can cause unexpected behavior we now show a warning in development for them. #### Possible Ways to Fix It @@ -16,4 +15,4 @@ Look for any usage of `next/link` or `next/router` that is being passed a non-in ### Useful Links -- [Routing section in Documentation](https://nextjs.org/docs#routing) +- [Routing section in Documentation](https://nextjs.org/docs/routing/introduction) diff --git a/errors/invalid-i18n-config.md b/errors/invalid-i18n-config.md new file mode 100644 index 0000000000000..b840d14a61f98 --- /dev/null +++ b/errors/invalid-i18n-config.md @@ -0,0 +1,44 @@ +# Invalid i18n config + +#### Why This Error + +In your `next.config.js` file you provided an invalid config for the `i18n` field. This could mean the limit for 100 locale items was exceeded. + +#### Possible Ways to Fix It + +Make sure your `i18n` field follows the allowed config shape, limits, and values: + +```js +module.exports = { + i18n: { + // These are all the locales you want to support in + // your application + locales: ['en-US', 'es', 'fr', 'nl-NL'], + // This is the default locale you want to be used when visiting + // a non-locale prefixed path e.g. `/hello` + defaultLocale: 'en-US', + // This is a list of locale domains and the default locale they + // should handle (these are only required when setting up domain routing) + domains: [ + { + domain: 'example.com', + defaultLocale: 'en-US', + // other locales that should be handled on this domain + locales: ['es'], + }, + { + domain: 'example.nl', + defaultLocale: 'nl-NL', + }, + { + domain: 'example.fr', + defaultLocale: 'fr', + }, + ], + }, +} +``` + +### Useful Links + +- [Internationalized Routing Documentation](https://nextjs.org/docs/advanced-features/i18n-routing) diff --git a/errors/invalid-images-config.md b/errors/invalid-images-config.md new file mode 100644 index 0000000000000..febbc5ee4adad --- /dev/null +++ b/errors/invalid-images-config.md @@ -0,0 +1,33 @@ +# Invalid images config + +#### Why This Error Occurred + +In your `next.config.js` file you provided an invalid config for the `images` field. + +#### Possible Ways to Fix It + +Make sure your `images` field follows the allowed config shape and values: + +```js +module.exports = { + images: { + // limit of 25 deviceSizes values + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + // limit of 25 imageSizes values + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + // limit of 50 domains values + domains: [], + path: '/_next/image', + // loader can be 'default', 'imgix', 'cloudinary', 'akamai', or 'custom' + loader: 'default', + // disable static imports for image files + disableStaticImages: false, + // minimumCacheTTL is in seconds, must be integer 0 or more + minimumCacheTTL: 60, + }, +} +``` + +### Useful Links + +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) diff --git a/errors/invalid-page-config.md b/errors/invalid-page-config.md index a71fb1862d668..6984a9bb1b9e5 100644 --- a/errors/invalid-page-config.md +++ b/errors/invalid-page-config.md @@ -6,7 +6,7 @@ In one of your pages you did `export const config` with an invalid value. #### Possible Ways to Fix It -The page's config must be an object initialized directly when being exported. +The page's config must be an object initialized directly when being exported and not modified dynamically. This is not allowed @@ -14,6 +14,19 @@ This is not allowed export const config = 'hello world' ``` +This is not allowed + +```js +const config = {} +config.amp = true +``` + +This is not allowed + +```js +export { config } from '../config' +``` + This is allowed ```js @@ -22,5 +35,5 @@ export const config = { amp: true } ### Useful Links -- [Enabling AMP Support](https://nextjs.org/docs#enabling-amp-support) -- [API Middlewares](https://nextjs.org/docs#api-middlewares) +- [Enabling AMP Support](https://nextjs.org/docs/advanced-features/amp-support/introduction) +- [API Middlewares](https://nextjs.org/docs/api-routes/api-middlewares) diff --git a/errors/invalid-redirect-gssp.md b/errors/invalid-redirect-gssp.md new file mode 100644 index 0000000000000..5b966ffa0a2df --- /dev/null +++ b/errors/invalid-redirect-gssp.md @@ -0,0 +1,32 @@ +# Invalid Redirect getStaticProps/getServerSideProps + +#### Why This Error Occurred + +The `redirect` value returned from your `getStaticProps` or `getServerSideProps` function had invalid values. + +#### Possible Ways to Fix It + +Make sure you return the proper values for the `redirect` value. + +```js +export const getStaticProps = ({ params }) => { + if (params.slug === 'deleted-post') { + return { + redirect: { + permanent: true, // or false + destination: '/some-location', + }, + } + } + + return { + props: { + // data + }, + } +} +``` + +### Useful Links + +- [Data Fetching Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) diff --git a/errors/invalid-relative-url-external-as.md b/errors/invalid-relative-url-external-as.md new file mode 100644 index 0000000000000..848b56d422e46 --- /dev/null +++ b/errors/invalid-relative-url-external-as.md @@ -0,0 +1,47 @@ +# Invalid relative `href` and external `as` values + +#### Why This Error Occurred + +Somewhere you are utilizing the `next/link` component, `Router#push`, or `Router#replace` with a relative route in your `href` that has an external `as` value. The `as` value must be relative also or only `href` should be used with an external URL. + +Note: this error will only show when the `next/link` component is clicked not when only rendered. + +**Incompatible `href` and `as`** + +```jsx +import Link from 'next/link' + +export default function Page(props) { + return ( + <> + + Invalid link + + + ) +} +``` + +**Compatible `href` and `as`** + +```jsx +import Link from 'next/link' + +export default function Page(props) { + return ( + <> + + Invalid link + + + ) +} +``` + +#### Possible Ways to Fix It + +Look for any usage of the `next/link` component, `Router#push`, or `Router#replace` and make sure that the provided `href` and `as` values are compatible + +### Useful Links + +- [Routing section in Documentation](https://nextjs.org/docs/routing/introduction) diff --git a/errors/invalid-route-source.md b/errors/invalid-route-source.md index 07f1773427266..c29c8610cf65c 100644 --- a/errors/invalid-route-source.md +++ b/errors/invalid-route-source.md @@ -29,4 +29,4 @@ Wrap the `RegExp` part of your `source` as an un-named parameter. ### Useful Links - [path-to-regexp](https://github.com/pillarjs/path-to-regexp) -- [un-named paramters](https://github.com/pillarjs/path-to-regexp#unnamed-parameters) +- [un-named parameters](https://github.com/pillarjs/path-to-regexp#unnamed-parameters) diff --git a/errors/invalid-server-options.md b/errors/invalid-server-options.md new file mode 100644 index 0000000000000..b688e53942e8a --- /dev/null +++ b/errors/invalid-server-options.md @@ -0,0 +1,25 @@ +# It looks like the next instance is being instantiated incorrectly. + +#### Why This Error Occurred + +You have passed a null or undefined parameter to the next() call. + +#### Possible Ways to Fix It + +Make sure you are passing the variables properly: + +```js +const app = next() +``` + +And make sure you're passing dev as shown in the examples below: + +```js +const app = next({ dev: boolean }) +``` + +### Useful Links + +- [custom-server-express](https://github.com/vercel/next.js/blob/6ca00bfe312c8d3fc5c20d25a9cd8d2741a29332/examples/custom-server-express/server.js#L6) +- [custom-server](https://github.com/vercel/next.js/blob/6ca00bfe312c8d3fc5c20d25a9cd8d2741a29332/examples/custom-server/server.js#L6) +- [custom-server-typescript](https://github.com/vercel/next.js/blob/6ca00bfe312c8d3fc5c20d25a9cd8d2741a29332/examples/custom-server-typescript/server/index.ts#L7) diff --git a/errors/invalid-webpack-5-version.md b/errors/invalid-webpack-5-version.md new file mode 100644 index 0000000000000..5c10e71793c1b --- /dev/null +++ b/errors/invalid-webpack-5-version.md @@ -0,0 +1,13 @@ +# Invalid webpack 5 version + +#### Why This Error Occurred + +While leveraging webpack 5 support in Next.js the minimum required version of `v5.15.0` was not met. This version is needed while leveraging webpack 5 support with Next.js as early versions are missing patches that cause unexpected behavior. + +#### Possible Ways to Fix It + +Upgrade the version of webpack 5 being used with Next.js to at least `v5.15.0` by updating your resolutions field for `webpack` in your `package.json`. + +### Useful Links + +- [Yarn Selective Dependency Resolutions Documentation](https://classic.yarnpkg.com/en/docs/selective-version-resolutions/) diff --git a/errors/link-multiple-children.md b/errors/link-multiple-children.md new file mode 100644 index 0000000000000..54476589c6900 --- /dev/null +++ b/errors/link-multiple-children.md @@ -0,0 +1,36 @@ +# Multiple children were passed to + +#### Why This Error Occurred + +In your application code multiple children were passed to `next/link` but only one child is supported: + +For example: + +```js +import Link from 'next/link' + +export default function Home() { + return ( + + To About + Second To About + + ) +} +``` + +#### Possible Ways to Fix It + +Make sure only one child is used when using ``: + +```js +import Link from 'next/link' + +export default function Home() { + return ( + + To About + + ) +} +``` diff --git a/errors/link-passhref.md b/errors/link-passhref.md new file mode 100644 index 0000000000000..33fec658066e0 --- /dev/null +++ b/errors/link-passhref.md @@ -0,0 +1,32 @@ +# Link passHref + +### Why This Error Occurred + +`passHref` was not used for a `Link` component that wraps a custom component. This is needed in order to pass the `href` to the child `` tag. + +### Possible Ways to Fix It + +If you're using a custom component that wraps an `` tag, make sure to add `passHref`: + +```jsx +import Link from 'next/link' +import styled from 'styled-components' + +const StyledLink = styled.a` + color: red; +` + +function NavLink({ href, name }) { + return ( + + {name} + + ) +} + +export default NavLink +``` + +### Useful Links + +- [next/link - Custom Component](https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-custom-component-that-wraps-an-a-tag) diff --git a/errors/manifest.json b/errors/manifest.json new file mode 100644 index 0000000000000..f1580c3b658fd --- /dev/null +++ b/errors/manifest.json @@ -0,0 +1,465 @@ +{ + "routes": [ + { + "title": "Messages", + "heading": true, + "routes": [ + { + "title": "404-get-initial-props", + "path": "/errors/404-get-initial-props.md" + }, + { "title": "amp-bind-jsx-alt", "path": "/errors/amp-bind-jsx-alt.md" }, + { + "title": "amp-export-validation", + "path": "/errors/amp-export-validation.md" + }, + { + "title": "api-routes-body-size-limit", + "path": "/errors/api-routes-body-size-limit.md" + }, + { + "title": "api-routes-static-export", + "path": "/errors/api-routes-static-export.md" + }, + { + "title": "app-container-deprecated", + "path": "/errors/app-container-deprecated.md" + }, + { + "title": "build-dir-not-writeable", + "path": "/errors/build-dir-not-writeable.md" + }, + { + "title": "built-in-css-disabled", + "path": "/errors/built-in-css-disabled.md" + }, + { + "title": "can-not-output-to-public", + "path": "/errors/can-not-output-to-public.md" + }, + { + "title": "can-not-output-to-static", + "path": "/errors/can-not-output-to-static.md" + }, + { + "title": "cant-override-next-props", + "path": "/errors/cant-override-next-props.md" + }, + { + "title": "circular-structure", + "path": "/errors/circular-structure.md" + }, + { + "title": "config-resolve-alias", + "path": "/errors/config-resolve-alias.md" + }, + { + "title": "conflicting-amp-tag", + "path": "/errors/conflicting-amp-tag.md" + }, + { + "title": "conflicting-public-file-page", + "path": "/errors/conflicting-public-file-page.md" + }, + { + "title": "conflicting-ssg-paths", + "path": "/errors/conflicting-ssg-paths.md" + }, + { "title": "css-global", "path": "/errors/css-global.md" }, + { "title": "css-modules-npm", "path": "/errors/css-modules-npm.md" }, + { "title": "css-npm", "path": "/errors/css-npm.md" }, + { + "title": "custom-error-no-custom-404", + "path": "/errors/custom-error-no-custom-404.md" + }, + { + "title": "doc-crossorigin-deprecated", + "path": "/errors/doc-crossorigin-deprecated.md" + }, + { "title": "duplicate-sass", "path": "/errors/duplicate-sass.md" }, + { + "title": "empty-configuration", + "path": "/errors/empty-configuration.md" + }, + { + "title": "empty-object-getInitialProps", + "path": "/errors/empty-object-getInitialProps.md" + }, + { + "title": "env-key-not-allowed", + "path": "/errors/env-key-not-allowed.md" + }, + { + "title": "env-loading-disabled", + "path": "/errors/env-loading-disabled.md" + }, + { + "title": "export-all-in-page", + "path": "/errors/export-all-in-page.md" + }, + { "title": "export-image-api", "path": "/errors/export-image-api.md" }, + { + "title": "export-no-custom-routes", + "path": "/errors/export-no-custom-routes.md" + }, + { + "title": "export-path-mismatch", + "path": "/errors/export-path-mismatch.md" + }, + { + "title": "generatebuildid-not-a-string", + "path": "/errors/generatebuildid-not-a-string.md" + }, + { + "title": "google-font-display", + "path": "/errors/google-font-display.md" + }, + { + "title": "google-font-preconnect", + "path": "/errors/google-font-preconnect.md" + }, + { + "title": "get-initial-props-as-an-instance-method", + "path": "/errors/get-initial-props-as-an-instance-method.md" + }, + { + "title": "gsp-redirect-during-prerender", + "path": "/errors/gsp-redirect-during-prerender.md" + }, + { + "title": "gssp-component-member", + "path": "/errors/gssp-component-member.md" + }, + { "title": "gssp-export", "path": "/errors/gssp-export.md" }, + { + "title": "gssp-mixed-not-found-redirect", + "path": "/errors/gssp-mixed-not-found-redirect.md" + }, + { "title": "head-build-id", "path": "/errors/head-build-id.md" }, + { + "title": "href-interpolation-failed", + "path": "/errors/href-interpolation-failed.md" + }, + { "title": "improper-devtool", "path": "/errors/improper-devtool.md" }, + { + "title": "incompatible-href-as", + "path": "/errors/incompatible-href-as.md" + }, + { + "title": "inline-script-id", + "path": "/errors/inline-script-id.md" + }, + { "title": "install-sass", "path": "/errors/install-sass.md" }, + { "title": "install-sharp", "path": "/errors/install-sharp.md" }, + { + "title": "invalid-assetprefix", + "path": "/errors/invalid-assetprefix.md" + }, + { + "title": "invalid-dynamic-suspense", + "path": "/errors/invalid-dynamic-suspense.md" + }, + { + "title": "invalid-external-rewrite", + "path": "/errors/invalid-external-rewrite.md" + }, + { + "title": "invalid-getstaticpaths-value", + "path": "/errors/invalid-getstaticpaths-value.md" + }, + { + "title": "invalid-getstaticprops-value", + "path": "/errors/invalid-getstaticprops-value.md" + }, + { + "title": "invalid-href-passed", + "path": "/errors/invalid-href-passed.md" + }, + { + "title": "invalid-i18n-config", + "path": "/errors/invalid-i18n-config.md" + }, + { + "title": "invalid-images-config", + "path": "/errors/invalid-images-config.md" + }, + { + "title": "invalid-multi-match", + "path": "/errors/invalid-multi-match.md" + }, + { + "title": "invalid-page-config", + "path": "/errors/invalid-page-config.md" + }, + { + "title": "invalid-react-version", + "path": "/errors/invalid-react-version.md" + }, + { + "title": "invalid-redirect-gssp", + "path": "/errors/invalid-redirect-gssp.md" + }, + { + "title": "invalid-relative-url-external-as", + "path": "/errors/invalid-relative-url-external-as.md" + }, + { + "title": "invalid-resolve-alias", + "path": "/errors/invalid-resolve-alias.md" + }, + { + "title": "invalid-route-source", + "path": "/errors/invalid-route-source.md" + }, + { + "title": "invalid-server-options", + "path": "/errors/invalid-server-options.md" + }, + { + "title": "invalid-webpack-5-version", + "path": "/errors/invalid-webpack-5-version.md" + }, + { + "title": "link-passhref", + "path": "/errors/link-passhref.md" + }, + { "title": "manifest.json", "path": "/errors/manifest.json" }, + { + "title": "minification-disabled", + "path": "/errors/minification-disabled.md" + }, + { + "title": "missing-document-component", + "path": "/errors/missing-document-component.md" + }, + { + "title": "missing-env-value", + "path": "/errors/missing-env-value.md" + }, + { "title": "multi-tabs", "path": "/errors/multi-tabs.md" }, + { + "title": "nested-reserved-page", + "path": "/errors/nested-reserved-page.md" + }, + { + "title": "next-dynamic-modules", + "path": "/errors/next-dynamic-modules.md" + }, + { + "title": "next-export-no-build-id", + "path": "/errors/next-export-no-build-id.md" + }, + { + "title": "next-export-serverless", + "path": "/errors/next-export-serverless.md" + }, + { + "title": "next-head-count-missing", + "path": "/errors/next-head-count-missing.md" + }, + { + "title": "next-image-missing-loader", + "path": "/errors/next-image-missing-loader.md" + }, + { + "title": "next-image-missing-loader-width", + "path": "/errors/next-image-missing-loader-width.md" + }, + { + "title": "next-image-unconfigured-host", + "path": "/errors/next-image-unconfigured-host.md" + }, + { + "title": "next-start-serverless", + "path": "/errors/next-start-serverless.md" + }, + { "title": "no-cache", "path": "/errors/no-cache.md" }, + { "title": "no-css-tags", "path": "/errors/no-css-tags.md" }, + { + "title": "no-document-import-in-page", + "path": "/errors/no-document-import-in-page.md" + }, + { + "title": "no-document-title", + "path": "/errors/no-document-title.md" + }, + { + "title": "no-document-viewport-meta", + "path": "/errors/no-document-viewport-meta.md" + }, + { + "title": "no-duplicate-head", + "path": "/errors/no-duplicate-head.md" + }, + { + "title": "no-head-import-in-document", + "path": "/errors/no-head-import-in-document.md" + }, + { + "title": "no-html-link-for-pages", + "path": "/errors/no-html-link-for-pages.md" + }, + { + "title": "no-on-app-updated-hook", + "path": "/errors/no-on-app-updated-hook.md" + }, + { + "title": "no-page-custom-font", + "path": "/errors/no-page-custom-font.md" + }, + { + "title": "no-router-instance", + "path": "/errors/no-router-instance.md" + }, + { + "title": "no-sync-scripts", + "path": "/errors/no-sync-scripts.md" + }, + { + "title": "no-title-in-document-head", + "path": "/errors/no-title-in-document-head.md" + }, + { + "title": "no-unwanted-polyfillio", + "path": "/errors/no-unwanted-polyfillio.md" + }, + { + "title": "non-standard-node-env", + "path": "/errors/non-standard-node-env.md" + }, + { + "title": "opt-out-auto-static-optimization", + "path": "/errors/opt-out-auto-static-optimization.md" + }, + { + "title": "opt-out-automatic-prerendering", + "path": "/errors/opt-out-automatic-prerendering.md" + }, + { + "title": "page-without-valid-component", + "path": "/errors/page-without-valid-component.md" + }, + { + "title": "popstate-state-empty", + "path": "/errors/popstate-state-empty.md" + }, + { "title": "postcss-function", "path": "/errors/postcss-function.md" }, + { + "title": "postcss-ignored-plugin", + "path": "/errors/postcss-ignored-plugin.md" + }, + { "title": "postcss-shape", "path": "/errors/postcss-shape.md" }, + { + "title": "prefetch-true-deprecated", + "path": "/errors/prefetch-true-deprecated.md" + }, + { "title": "prerender-error", "path": "/errors/prerender-error.md" }, + { + "title": "production-start-no-build-id", + "path": "/errors/production-start-no-build-id.md" + }, + { + "title": "promise-in-next-config", + "path": "/errors/promise-in-next-config.md" + }, + { + "title": "public-next-folder-conflict", + "path": "/errors/public-next-folder-conflict.md" + }, + { "title": "react-version", "path": "/errors/react-version.md" }, + { + "title": "render-no-starting-slash", + "path": "/errors/render-no-starting-slash.md" + }, + { + "title": "reserved-page-prop", + "path": "/errors/reserved-page-prop.md" + }, + { + "title": "rewrite-auto-export-fallback", + "path": "/errors/rewrite-auto-export-fallback.md" + }, + { + "title": "routes-must-be-array", + "path": "/errors/routes-must-be-array.md" + }, + { + "title": "ssg-fallback-true-export", + "path": "/errors/ssg-fallback-true-export.md" + }, + { + "title": "static-dir-deprecated", + "path": "/errors/static-dir-deprecated.md" + }, + { "title": "threw-undefined", "path": "/errors/threw-undefined.md" }, + { + "title": "undefined-webpack-config", + "path": "/errors/undefined-webpack-config.md" + }, + { "title": "url-deprecated", "path": "/errors/url-deprecated.md" }, + { "title": "webpack5", "path": "/errors/webpack5.md" }, + { + "title": "client-side-exception-occurred", + "path": "/errors/client-side-exception-occurred.md" + }, + { + "title": "future-webpack5-moved-to-webpack5", + "path": "/errors/future-webpack5-moved-to-webpack5.md" + }, + { + "title": "link-multiple-children", + "path": "/errors/link-multiple-children.md" + }, + { "title": "no-img-element", "path": "/errors/no-img-element.md" }, + { + "title": "non-dynamic-getstaticpaths-usage", + "path": "/errors/non-dynamic-getstaticpaths-usage.md" + }, + { + "title": "placeholder-blur-data-url", + "path": "/errors/placeholder-blur-data-url.md" + }, + { + "title": "import-esm-externals", + "path": "/errors/import-esm-externals.md" + }, + { + "title": "static-page-generation-timeout", + "path": "/errors/static-page-generation-timeout.md" + }, + { + "title": "page-data-collection-timeout", + "path": "/errors/page-data-collection-timeout.md" + }, + { + "title": "sharp-missing-in-production", + "path": "/errors/sharp-missing-in-production.md" + }, + { + "title": "script-in-document-page", + "path": "/errors/no-script-in-document-page.md" + }, + { + "title": "script-in-head-component", + "path": "/errors/no-script-in-head-component.md" + }, + { + "title": "max-custom-routes-reached", + "path": "/errors/max-custom-routes-reached.md" + }, + { + "title": "module-not-found", + "path": "/errors/module-not-found.md" + }, + { + "title": "next-config-error", + "path": "/errors/next-config-error.md" + }, + { + "title": "invalid-api-status-body", + "path": "/errors/invalid-api-status-body.md" + } + ] + } + ] +} diff --git a/errors/max-custom-routes-reached.md b/errors/max-custom-routes-reached.md new file mode 100644 index 0000000000000..94398ff987774 --- /dev/null +++ b/errors/max-custom-routes-reached.md @@ -0,0 +1,17 @@ +# Max Custom Routes Reached + +#### Why This Error Occurred + +The number of combined routes from `headers`, `redirects`, and `rewrites` exceeds 1000. This can impact performance because each request will iterate over all routes to check for a match in the worst case. + +#### Possible Ways to Fix It + +- Leverage dynamic routes inside of the `pages` folder to reduce the number of rewrites needed +- Combine headers routes into dynamic matches e.g. `/first-header-route` `/second-header-route` -> `/(first-header-route$|second-header-route$)` + +### Useful Links + +- [Dynamic Routes documentation](https://nextjs.org/docs/routing/dynamic-routes) +- [Rewrites documentation](https://nextjs.org/docs/api-reference/next.config.js/rewrites) +- [Redirects documentation](https://nextjs.org/docs/api-reference/next.config.js/redirects) +- [Headers documentation](https://nextjs.org/docs/api-reference/next.config.js/headers) diff --git a/errors/missing-document-component.md b/errors/missing-document-component.md new file mode 100644 index 0000000000000..74ab4d738a159 --- /dev/null +++ b/errors/missing-document-component.md @@ -0,0 +1,38 @@ +# Missing Document Components + +#### Why This Error Occurred + +In your custom `pages/_document` an expected sub-component was not rendered. + +#### Possible Ways to Fix It + +Make sure to import and render all of the expected `Document` components: + +- `` +- `` +- `
    ` +- `` + +For example: + +```tsx +import Document, { Html, Head, Main, NextScript } from 'next/document' + +export default class MyDocument extends Document { + render() { + return ( + + + +
    + + + + ) + } +} +``` + +### Useful Links + +- [Custom Document Docs](https://nextjs.org/docs/advanced-features/custom-document) diff --git a/errors/module-not-found.md b/errors/module-not-found.md new file mode 100644 index 0000000000000..5cd6401e0e135 --- /dev/null +++ b/errors/module-not-found.md @@ -0,0 +1,151 @@ +# Module Not Found + +#### Why This Error Occurred + +A module not found error can occur for many different reasons: + +- The module you're trying to import is not installed in your dependencies +- The module you're trying to import is in a different directory +- The module you're trying to import has a different casing +- The module you're trying to import uses Node.js specific modules, for example `dns`, outside of `getStaticProps` / `getStaticPaths` / `getServerSideProps` + +#### Possible Ways to Fix It + +##### The module you're trying to import is not installed in your dependencies + +When importing a module from [npm](https://npmjs.com) this module has to be installed locally. + +For example when importing the `swr` package: + +```js +import useSWR from 'swr' +``` + +The `swr` module has to be installed using a package manager. + +- When using `npm`: `npm install swr` +- When using `yarn`: `yarn add swr` + +##### The module you're trying to import is in a different directory + +Make sure that the path you're importing refers to the right directory and file. + +##### The module you're trying to import has a different casing + +Make sure the casing of the file is correct. + +Example: + +```js +// components/MyComponent.js +export default function MyComponent() { + return

    Hello

    +} +``` + +```js +// pages/index.js +// Note how `components/MyComponent` exists but `Mycomponent` without the capital `c` is imported +import MyComponent from '../components/Mycomponent' +``` + +Incorrect casing will lead to build failures on case-sensitive environments like most Linux-based continuous integration and can cause issues with Fast Refresh. + +##### The module you're trying to import uses Node.js specific modules + +`getStaticProps`, `getStaticPaths`, and `getServerSideProps` allow for using modules that can only run in the Node.js environment. This allows you to do direct database queries or reading data from Redis to name a few examples. + +The tree shaking only runs on top level pages, so it can't be relied on in separate React components. + +You can verify the tree shaking on [next-code-elimination.vercel.app](https://next-code-elimination.vercel.app/). + +Example of correctly tree shaken code: + +```js +// lib/redis.js +import Redis from 'ioredis' + +const redis = new Redis(process.env.REDIS_URL) + +export default redis +``` + +```js +// pages/index.js +import redis from '../lib/redis' + +export async function getStaticProps() { + const message = await redis.get('message') + return { + message, + } +} + +export default function Home({ message }) { + return

    {message}

    +} +``` + +Example of code that would break: + +```js +// lib/redis.js +import Redis from 'ioredis' + +const redis = new Redis(process.env.REDIS_URL) + +export default redis +``` + +```js +// pages/index.js +// Redis is a Node.js specific library that can't run in the browser +// Trying to use it in code that runs on both Node.js and the browser will result in a module not found error for modules that ioredis relies on +// If you run into such an error it's recommended to move the code to `getStaticProps` or `getServerSideProps` as those methods guarantee that the code is only run in Node.js. +import redis from '../lib/redis' +import { useEffect, useState } from 'react' + +export default function Home() { + const [message, setMessage] = useState() + useEffect(() => { + redis.get('message').then((result) => { + setMessage(result) + }) + }, []) + return

    {message}

    +} +``` + +Example of code that would break: + +```js +// lib/redis.js +import Redis from 'ioredis' + +// Modules that hold Node.js-only code can't also export React components +// Tree shaking of getStaticProps/getStaticPaths/getServerSideProps is ran only on page files +const redis = new Redis(process.env.REDIS_URL) + +export function MyComponent() { + return

    Hello

    +} + +export default redis +``` + +```js +// pages/index.js +// In practice you'll want to refactor the `MyComponent` to be a separate file so that tree shaking ensures that specific import is not included for the browser compilation +import redis, { MyComponent } from '../lib/redis' + +export async function getStaticProps() { + const message = await redis.get('message') + return { + message, + } +} + +export default function Home() { + return +} +``` diff --git a/errors/next-config-error.md b/errors/next-config-error.md new file mode 100644 index 0000000000000..d5e5b8b146387 --- /dev/null +++ b/errors/next-config-error.md @@ -0,0 +1,14 @@ +# next.config.js Loading Error + +#### Why This Error Occurred + +When attempting to load your `next.config.js` file an error occurred. This could be due to a syntax error or attempting to `require` a module that wasn't available. + +#### Possible Ways to Fix It + +See the error message in your terminal where you started `next` to see more context. The `next.config.js` file is not transpiled by Next.js currently so ensure only features supported by your current node.js version are being used. + +### Useful Links + +- [next.config.js documentation](https://nextjs.org/docs/api-reference/next.config.js/introduction) +- [node.js version feature chart](https://node.green/) diff --git a/errors/next-export-no-build-id.md b/errors/next-export-no-build-id.md new file mode 100644 index 0000000000000..affed88c30803 --- /dev/null +++ b/errors/next-export-no-build-id.md @@ -0,0 +1,10 @@ +# Could not find a production build + +#### Why This Error Occurred + +When running `next export` a production build is needed. + +#### Possible Ways to Fix It + +- Run `next build` to create a production build before running `next export`. +- If your intention was to run the development server run `next dev` instead. diff --git a/errors/next-head-count-missing.md b/errors/next-head-count-missing.md index 78891e0864194..40c040f319461 100644 --- a/errors/next-head-count-missing.md +++ b/errors/next-head-count-missing.md @@ -6,6 +6,6 @@ You have a custom `pages/_document.js` that doesn't have the components required #### Possible Ways to Fix It -Ensure that your `_document.js` is importing and rendering all of the [required components](https://nextjs.org/docs#custom-document). +Ensure that your `_document.js` is importing and rendering all of the [required components](https://nextjs.org/docs/advanced-features/custom-document). In this case you are most likely not rendering the `` component imported from `next/document`. diff --git a/errors/next-image-missing-loader-width.md b/errors/next-image-missing-loader-width.md new file mode 100644 index 0000000000000..0df5c6efa7124 --- /dev/null +++ b/errors/next-image-missing-loader-width.md @@ -0,0 +1,17 @@ +# Missing `width` in the URL Returned by the Loader Prop on `next/image` + +#### Why This Error Occurred + +The [`loader`](https://nextjs.org/docs/api-reference/next/image#loader) prop on the `next/image` component allows you to override the built-in URL resolution with a custom implementation in order to support any 3rd party cloud provider that can perform Image Optimization. + +This error occurred because the provided `loader()` function did not use `width` in the returned URL string. This means that the image will likely not be resized and therefore degrade performance. + +#### Possible Ways to Fix It + +- Ensure your Image Optimization provider can resize images. Then use the `width` parameter from the [`loader()`](https://nextjs.org/docs/api-reference/next/image#loader) function to construct the correct URL string. +- Add the [`unoptimized`](https://nextjs.org/docs/api-reference/next/image#unoptimized) prop. + +### Useful Links + +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) +- [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) diff --git a/errors/next-image-missing-loader.md b/errors/next-image-missing-loader.md new file mode 100644 index 0000000000000..9a03dcb26a3d9 --- /dev/null +++ b/errors/next-image-missing-loader.md @@ -0,0 +1,15 @@ +# Missing `loader` Prop on `next/image` + +#### Why This Error Occurred + +When using the `next/image` component with [`loader="custom"`](https://nextjs.org/docs/basic-features/image-optimization#loader) in `next.config.js`, you must provide the [`loader`](https://nextjs.org/docs/api-reference/next/image#loader) prop to the component with your custom implementation. + +#### Possible Ways to Fix It + +- Add the [`loader`](https://nextjs.org/docs/api-reference/next/image#loader) prop to all usages of the `next/image` component. +- Change the [`loader`](https://nextjs.org/docs/basic-features/image-optimization#loader) configuration in `next.config.js`. + +### Useful Links + +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) +- [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) diff --git a/errors/next-image-unconfigured-host.md b/errors/next-image-unconfigured-host.md new file mode 100644 index 0000000000000..e69525ad03d2b --- /dev/null +++ b/errors/next-image-unconfigured-host.md @@ -0,0 +1,22 @@ +# next/image Un-configured Host + +#### Why This Error Occurred + +On one of your pages that leverages the `next/image` component, you passed a `src` value that uses a hostname in the URL that isn't defined in the `images.domains` config in `next.config.js`. + +#### Possible Ways to Fix It + +Add the hostname of your URL to the `images.domains` config in `next.config.js`: + +```js +// next.config.js +module.exports = { + images: { + domains: ['assets.example.com'], + }, +} +``` + +### Useful Links + +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) diff --git a/errors/next-script-for-ga.md b/errors/next-script-for-ga.md new file mode 100644 index 0000000000000..2f367dff127c3 --- /dev/null +++ b/errors/next-script-for-ga.md @@ -0,0 +1,98 @@ +# Next Script for Google Analytics + +### Why This Error Occurred + +An inline script was used for Google analytics which might impact your webpage's performance. + +### Possible Ways to Fix It + +#### Using gtag.js + +If you are using the [gtag.js](https://developers.google.com/analytics/devguides/collection/gtagjs) script to add analytics, use the `next/script` component with the right loading strategy to defer loading of the script until necessary. + +```jsx +import Script from 'next/script' + +const Home = () => { + return ( +
    + + +
    + ) +} + +export default Home +``` + +#### Using analytics.js + +If you are using the [analytics.js](https://developers.google.com/analytics/devguides/collection/analyticsjs) script to add analytics: + +```jsx +import Script from 'next/script' + +const Home = () => { + return ( +
    + +
    + ) +} + +export default Home +``` + +If you are using the [alternative async variant](https://developers.google.com/analytics/devguides/collection/analyticsjs#alternative_async_tag): + +```jsx +import Script from 'next/script' + +const Home = () => { + return ( +
    + + +
    Home Page
    +
    + ) +} + +export default Home +``` + +### Useful Links + +- [Efficiently load third-party JavaScript](https://web.dev/efficiently-load-third-party-javascript/) diff --git a/errors/no-title-in-document-head.md b/errors/no-title-in-document-head.md new file mode 100644 index 0000000000000..771e99674d5c4 --- /dev/null +++ b/errors/no-title-in-document-head.md @@ -0,0 +1,30 @@ +# No Title in Document Head + +### Why This Error Occurred + +A `` element was defined within the `Head` component imported from `next/document`, which should only be used for any `<head>` code that is common for all pages. Title tags should be defined at the page-level using `next/head`. + +### Possible Ways to Fix It + +Within a page or component, import and use `next/head` to define a page title: + +```jsx +import Head from 'next/head' + +export class Home { + render() { + return ( + <div> + <Head> + <title>My page title + + + ) + } +} +``` + +### Useful links + +- [next/head](https://nextjs.org/docs/api-reference/next/head) +- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document) diff --git a/errors/no-unwanted-polyfillio.md b/errors/no-unwanted-polyfillio.md new file mode 100644 index 0000000000000..6f05aa9a7251a --- /dev/null +++ b/errors/no-unwanted-polyfillio.md @@ -0,0 +1,13 @@ +# Duplicate Polyfills from Polyfill.io + +#### Why This Error Occurred + +You are using Polyfill.io and including duplicate polyfills already shipped with Next.js. This increases page weight unnecessarily which can affect loading performance. + +#### Possible Ways to Fix It + +Remove all duplicate polyfills that are included with Polyfill.io. If you need to add polyfills but are not sure if Next.js already includes it, take a look at the list of [supported browsers and features](https://nextjs.org/docs/basic-features/supported-browsers-features) first. + +### Useful Links + +- [Supported Browsers and Features](https://nextjs.org/docs/basic-features/supported-browsers-features) diff --git a/errors/non-dynamic-getstaticpaths-usage.md b/errors/non-dynamic-getstaticpaths-usage.md new file mode 100644 index 0000000000000..2f94c774b0cab --- /dev/null +++ b/errors/non-dynamic-getstaticpaths-usage.md @@ -0,0 +1,14 @@ +# getStaticPaths Used on Non-Dynamic Page + +#### Why This Error Occurred + +On a non-dynamic SSG page `getStaticPaths` was incorrectly exported as this can only be used on dynamic pages to return the paths to prerender. + +#### Possible Ways to Fix It + +Remove the `getStaticPaths` export on the non-dynamic page or rename the page to be a dynamic page. + +### Useful Links + +- [Dynamic Routes Documentation](https://nextjs.org/docs/routing/dynamic-routes) +- [`getStaticPaths` Documentation](https://nextjs.org/docs/routing/dynamic-routes) diff --git a/errors/non-standard-node-env.md b/errors/non-standard-node-env.md index 7655a8874d1ab..238823e622dd1 100644 --- a/errors/non-standard-node-env.md +++ b/errors/non-standard-node-env.md @@ -1,16 +1,28 @@ -# Non-Standard Node_env +# Non-Standard NODE_ENV #### Why This Error Occurred -In your environment you set a non-standard value for `NODE_ENV`. +Your environment has a non-standard `NODE_ENV` value configured. -Next.js automatically sets this environment value for you and also forces the correct value during bundling to ensure the bundles are optimized and code can be tree-shaken correctly. +This may be by accident, so if you're unaware where the value is coming from, check the following: -When you set a non-standard environment value like `staging` this causes inconsistent behavior since we override the value to the standard one during bundling e.g. `production` or `development`. +- The `.env*` files in your project, if present +- Your `~/.bash_profile`, if present +- Your `~/.zshrc`, if present + +The greater React ecosystem treats `NODE_ENV` as a convention, only permitting three (3) values: + +- `production`: When your application is built with `next build` +- `development`: When your application is ran with `next dev` +- `test`: When your application is being tested (e.g. `jest`) + +Setting a non-standard `NODE_ENV` value may cause dependencies to behave unexpectedly, or worse, **break dead code elimination**. #### Possible Ways to Fix It -Remove any custom `NODE_ENV` environment variables and let Next.js automatically set the correct value for you. +To fix this error, identify the source of the erroneous `NODE_ENV` value and get rid of it: Next.js automatically sets the correct value for you. + +If you need the concept of different environments in your application, e.g. `staging`, you should use a different environment variable name like `APP_ENV`. ### Useful Links diff --git a/errors/opt-out-auto-static-optimization.md b/errors/opt-out-auto-static-optimization.md index bebc71a6dce34..4c2115e84b957 100644 --- a/errors/opt-out-auto-static-optimization.md +++ b/errors/opt-out-auto-static-optimization.md @@ -2,18 +2,18 @@ #### Why This Warning Occurred -You are using `getInitialProps` in your [Custom ``](https://nextjs.org/docs#custom-app). +You are using `getInitialProps` in your [Custom ``](https://nextjs.org/docs/advanced-features/custom-app). -This causes **all non-getStaticProps pages** to be executed on the server -- disabling [Automatic Static Optimization](https://nextjs.org/docs#automatic-static-optimization). +This causes **all non-getStaticProps pages** to be executed on the server -- disabling [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization). #### Possible Ways to Fix It Be sure you meant to use `getInitialProps` in `pages/_app`! There are some valid use cases for this, but it is often better to handle `getInitialProps` on a _per-page_ basis. -Check for any [higher-order components](https://reactjs.org/docs/higher-order-components.html) that may have added `getInitialProps` to your [Custom ``](https://nextjs.org/docs#custom-app). +Check for any [higher-order components](https://reactjs.org/docs/higher-order-components.html) that may have added `getInitialProps` to your [Custom ``](https://nextjs.org/docs/advanced-features/custom-app). -If you previously copied the [Custom ``](https://nextjs.org/docs#custom-app) example, you may be able to remove your `getInitialProps`. +If you previously copied the [Custom ``](https://nextjs.org/docs/advanced-features/custom-app) example, you may be able to remove your `getInitialProps`. The following `getInitialProps` does nothing and may be removed: diff --git a/errors/opt-out-automatic-prerendering.md b/errors/opt-out-automatic-prerendering.md index afe98e98df4ef..f3db86439cc7f 100644 --- a/errors/opt-out-automatic-prerendering.md +++ b/errors/opt-out-automatic-prerendering.md @@ -2,16 +2,16 @@ #### Why This Warning Occurred -You are using `getInitialProps` in your [Custom ``](https://nextjs.org/docs#custom-app). +You are using `getInitialProps` in your [Custom ``](https://nextjs.org/docs/advanced-features/custom-app). -This causes **all pages** to be executed on the server -- disabling [Automatic Static Optimization](https://nextjs.org/docs#automatic-static-optimization). +This causes **all pages** to be executed on the server -- disabling [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization). #### Possible Ways to Fix It Be sure you meant to use `getInitialProps` in `pages/_app`! There are some valid use cases for this, but it is often better to handle `getInitialProps` on a _per-page_ basis. -If you previously copied the [Custom ``](https://nextjs.org/docs#custom-app) example, you may be able to remove your `getInitialProps`. +If you previously copied the [Custom ``](https://nextjs.org/docs/advanced-features/custom-app) example, you may be able to remove your `getInitialProps`. The following `getInitialProps` does nothing and may be removed: diff --git a/errors/page-data-collection-timeout.md b/errors/page-data-collection-timeout.md new file mode 100644 index 0000000000000..12332bfb430be --- /dev/null +++ b/errors/page-data-collection-timeout.md @@ -0,0 +1,18 @@ +# Collecting page data timed out after multiple attempts + +#### Why This Error Occurred + +Next.js tries to restart the worker pool of the page data collection when no progress happens for a while, to avoid hanging builds. + +When restarted it will retry all uncompleted jobs, but if a job was unsuccessfully attempted multiple times, this will lead to an error. + +#### Possible Ways to Fix It + +- Make sure that there is no infinite loop during execution. +- Make sure all Promises in `getStaticPaths` `resolve` or `reject` correctly. +- Avoid very long timeouts for network requests. +- Increase the timeout by changing the `experimental.pageDataCollectionTimeout` configuration option (default `60` in seconds). + +### Useful Links + +- [`getStaticPaths`](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation) diff --git a/errors/page-without-valid-component.md b/errors/page-without-valid-component.md index 9344b712c9dbb..09d94fee00c35 100644 --- a/errors/page-without-valid-component.md +++ b/errors/page-without-valid-component.md @@ -13,4 +13,4 @@ For each, you'll want to check if the file is meant to be a page. If the file is not meant to be a page, and instead, is a shared component or file, move the file to a different folder like `components` or `lib`. -If the file is meant to be a page, double check you have an `export default` with the React Component instead of an `export`. If you're already using `export default`, make sure the returned valid is a valid React Component. +If the file is meant to be a page, double check you have an `export default` with the React Component instead of an `export`. If you're already using `export default`, make sure the returned value is a valid React Component. diff --git a/errors/placeholder-blur-data-url.md b/errors/placeholder-blur-data-url.md new file mode 100644 index 0000000000000..2fc53099c9830 --- /dev/null +++ b/errors/placeholder-blur-data-url.md @@ -0,0 +1,15 @@ +# `placeholder=blur` without `blurDataURL` + +#### Why This Error Occurred + +You are attempting use the `next/image` component with `placeholder=blur` property but no `blurDataURL` property. + +The `blurDataURL` might be missing because you're using a string for `src` instead of a static import. + +Or `blurDataURL` might be missing because the static import is an unsupported image format. Only jpg, png, and webp are supported at this time. + +#### Possible Ways to Fix It + +- Add a [`blurDataURL`](https://nextjs.org/docs/api-reference/next/image#blurdataurl) property, the contents should be a small Data URL to represent the image +- Change the [`src`](https://nextjs.org/docs/api-reference/next/image#src) property to a static import with one of the supported file types: jpg, png, or webp +- Remove the [`placeholder`](https://nextjs.org/docs/api-reference/next/image#placeholder) property, effectively no blur effect diff --git a/errors/popstate-state-empty.md b/errors/popstate-state-empty.md index 1268e15c0b036..ccd98713a557d 100644 --- a/errors/popstate-state-empty.md +++ b/errors/popstate-state-empty.md @@ -2,12 +2,12 @@ #### Why This Error Occurred -When using the browser back button the popstate event is triggered. Next.js sets -`popstate` event triggered but `event.state` did not have `url` or `as`, causing a route change failure +When using the browser back button the popstate event is triggered. Next.js sees a +`popstate` event being triggered but `event.state` did not have `url` or `as`, causing a route change failure. #### Possible Ways to Fix It -The only known cause of this issue is manually manipulating `window.history` instead of using `next/router` +The only known cause of this issue is manually manipulating `window.history` instead of using `next/router`. Starting from version 9.5, Next.js will ignore `popstate` events that contain `event.state` not created by its own router. ### Useful Links diff --git a/errors/postcss-ignored-plugin.md b/errors/postcss-ignored-plugin.md index 658ad57f28575..e8c73f1c69965 100644 --- a/errors/postcss-ignored-plugin.md +++ b/errors/postcss-ignored-plugin.md @@ -17,4 +17,4 @@ Remove the plugin specified in the error message from your custom PostCSS config #### How do I configure CSS Modules? CSS Modules are supported in [Next.js' built-in CSS support](https://nextjs.org/docs/advanced-features/customizing-postcss-config). -You can [read more](https://nextjs.org/docs/advanced-features/customizing-postcss-config) about how to use them [here](https://nextjs.org/docs/advanced-features/customizing-postcss-config). +You can [read more about how to use CSS Modules here](https://nextjs.org/docs/advanced-features/customizing-postcss-config). diff --git a/errors/postcss-shape.md b/errors/postcss-shape.md index 889fe55bcbc68..f121c889d04b1 100644 --- a/errors/postcss-shape.md +++ b/errors/postcss-shape.md @@ -39,7 +39,7 @@ module.exports = { } ``` -You can [read more](https://nextjs.org/docs/advanced-features/customizing-postcss-config) about configuring PostCSS in Next.js [here](https://nextjs.org/docs/advanced-features/customizing-postcss-config). +You can [read more about configuring PostCSS in Next.js here](https://nextjs.org/docs/advanced-features/customizing-postcss-config). #### Common Errors diff --git a/errors/prefetch-true-deprecated.md b/errors/prefetch-true-deprecated.md index dbc5a2ed346f4..77733fea94726 100644 --- a/errors/prefetch-true-deprecated.md +++ b/errors/prefetch-true-deprecated.md @@ -18,4 +18,4 @@ These requests have low-priority and yield to fetch() or XHR requests. Next.js w The prefetch attribute is no longer needed, when set to true, example: `prefetch={true}`, remove the property. -Prefetching can be disabled with `prefetch={false}`. +Prefetching can be turned off with `prefetch={false}`. diff --git a/errors/prerender-error.md b/errors/prerender-error.md index 207671f546490..87e301c459db6 100644 --- a/errors/prerender-error.md +++ b/errors/prerender-error.md @@ -7,5 +7,7 @@ While prerendering a page an error occurred. This can occur for many reasons fro #### Possible Ways to Fix It - Make sure to move any non-pages out of the `pages` folder -- Check for any code that assumes a prop is available even when it might not be +- Check for any code that assumes a prop is available even when it might not be. e.g., have default data for all dynamic pages' props. - Check for any out of date modules that you might be relying on +- Make sure your component handles `fallback` if it is enabled in `getStaticPaths`. [Fallback docs](https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required) +- Make sure you are not trying to export (`next export`) pages that have server-side rendering enabled [(getServerSideProps)](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) diff --git a/errors/production-start-no-build-id.md b/errors/production-start-no-build-id.md new file mode 100644 index 0000000000000..c6c9bfdb93f57 --- /dev/null +++ b/errors/production-start-no-build-id.md @@ -0,0 +1,10 @@ +# Could not find a production build + +#### Why This Error Occurred + +When running `next start` or a custom server in production mode a production build is needed. + +#### Possible Ways to Fix It + +- Run `next build` to create a production build before booting up the production server. +- If your intention was to run the development server run `next dev` instead. diff --git a/errors/react-version.md b/errors/react-version.md new file mode 100644 index 0000000000000..713254102491b --- /dev/null +++ b/errors/react-version.md @@ -0,0 +1,51 @@ +# Minimum React Version + +#### Why This Error Occurred + +Your project is using an old version of `react` or `react-dom` that does not +meet the suggested minimum version requirement. + +Next.js suggests using, at a minimum, `react@17.0.2` and `react-dom@17.0.2`. +Older versions of `react` and `react-dom` do work with Next.js, however, they do +not enable all of Next.js' features. + +For example, the following features are not enabled with old React versions: + +- [Fast Refresh](https://nextjs.org/docs/basic-features/fast-refresh): instantly + view edits to your app without losing component state +- Component stack trace in development: see the component tree that lead up to + an error +- Hydration mismatch warnings: trace down discrepancies in your React tree that + cause performance problems + +This list is not exhaustive, but illustrative in the value of upgrading React! + +#### Possible Ways to Fix It + +**Via npm** + +```bash +npm upgrade react@latest react-dom@latest +``` + +**Via Yarn** + +```bash +yarn add react@latest react-dom@latest +``` + +**Manually** Open your `package.json` and upgrade `react` and `react-dom`: + +```json +{ + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } +} +``` + +### Useful Links + +- [Fast Refresh blog post](https://nextjs.org/blog/next-9-4#fast-refresh) +- [Fast Refresh docs](https://nextjs.org/docs/basic-features/fast-refresh) diff --git a/errors/rewrite-auto-export-fallback.md b/errors/rewrite-auto-export-fallback.md new file mode 100644 index 0000000000000..a16fa7125639d --- /dev/null +++ b/errors/rewrite-auto-export-fallback.md @@ -0,0 +1,18 @@ +# Rewriting to Auto Export or Fallback Dynamic Route + +#### Why This Error Occurred + +One of your rewrites in your `next.config.js` point to a [dynamic route](https://nextjs.org/docs/routing/dynamic-routes) that is automatically statically optimized or is a [fallback SSG page](https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required). + +Rewriting to these pages are not yet supported since rewrites are not available client-side and the dynamic route params are unable to be parsed. Support for this may be added in a future release. + +#### Possible Ways to Fix It + +For fallback SSG pages you can add the page to the list of [prerendered paths](https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required). + +For static dynamic routes, you will currently need to either rewrite to non-dynamic route or opt the page out of the static optimization with [`getServerSideProps`](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) + +### Useful Links + +- [Dynamic Routes Documentation](https://nextjs.org/docs/routing/dynamic-routes) +- [Fallback Documentation](https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required) diff --git a/errors/routes-must-be-array.md b/errors/routes-must-be-array.md index fd4a909dc111c..59b1875653ec6 100644 --- a/errors/routes-must-be-array.md +++ b/errors/routes-must-be-array.md @@ -13,13 +13,11 @@ Make sure to return an array that contains the routes. ```js // next.config.js module.exports = { - experimental: { - async rewrites() { - return { - source: '/feedback', - destination: '/feedback/general', - } - }, + async rewrites() { + return { + source: '/feedback', + destination: '/feedback/general', + } }, } ``` @@ -28,15 +26,13 @@ module.exports = { ```js module.exports = { - experimental: { - async rewrites() { - return [ - { - source: '/feedback', - destination: '/feedback/general', - }, - ] - }, + async rewrites() { + return [ + { + source: '/feedback', + destination: '/feedback/general', + }, + ] }, } ``` diff --git a/errors/sharp-missing-in-production.md b/errors/sharp-missing-in-production.md new file mode 100644 index 0000000000000..02face5fc054f --- /dev/null +++ b/errors/sharp-missing-in-production.md @@ -0,0 +1,18 @@ +# Sharp Missing In Production + +#### Why This Error Occurred + +The `next/image` component's default loader uses [`squoosh`](https://www.npmjs.com/package/@squoosh/lib) because it is quick to install and suitable for a development environment. For a production environment using `next start`, it is strongly recommended you install [`sharp`](https://www.npmjs.com/package/sharp) by running `yarn add sharp` in your project directory. + +You are seeing this error because Image Optimization in production mode (`next start`) was detected. + +#### Possible Ways to Fix It + +- Install `sharp` by running `yarn add sharp` in your project directory and then reboot the server by running `next start` again +- If `sharp` is already installed but can't be resolved, set the `NEXT_SHARP_PATH` environment variable such as `NEXT_SHARP_PATH=/tmp/node_modules/sharp next start` + +> Note: This is not necessary for Vercel deployments, since `sharp` is installed automatically for you. + +### Useful Links + +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) diff --git a/errors/static-dir-deprecated.md b/errors/static-dir-deprecated.md index 303e7311c8b48..76e13986aed9a 100644 --- a/errors/static-dir-deprecated.md +++ b/errors/static-dir-deprecated.md @@ -8,11 +8,11 @@ The reason we want to support a `public` directory instead is to not require the #### Possible Ways to Fix It -You can move your `static` directory inside of the `public` directory and all URLs will remain the same as they were before. +You can move your `static` directory inside of the `public` directory and all URLs will stay the same as they were before. **Before** -```sh +``` static/ my-image.jpg pages/ @@ -23,7 +23,7 @@ components/ **After** -```sh +``` public/ static/ my-image.jpg @@ -35,4 +35,4 @@ components/ ### Useful Links -- [Static file serving docs](https://nextjs.org/docs#static-file-serving-eg-images) +- [Static file serving docs](https://nextjs.org/docs/basic-features/static-file-serving) diff --git a/errors/static-page-generation-timeout.md b/errors/static-page-generation-timeout.md new file mode 100644 index 0000000000000..6cef054dc5ddd --- /dev/null +++ b/errors/static-page-generation-timeout.md @@ -0,0 +1,19 @@ +# Static page generation timed out after multiple attempts + +#### Why This Error Occurred + +Next.js tries to restart the worker pool of the static page generation when no progress happens for a while, to avoid hanging builds. + +When restarted it will retry all uncompleted jobs, but if a job was unsuccessfully attempted multiple times, this will lead to an error. + +#### Possible Ways to Fix It + +- Make sure that there is no infinite loop during execution. +- Make sure all Promises in `getStaticPaths`/`getStaticProps` `resolve` or `reject` correctly. +- Avoid very long timeouts for network requests. +- Increase the timeout by changing the `experimental.staticPageGenerationTimeout` configuration option (default `60` in seconds). + +### Useful Links + +- [`getStaticPaths`](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation) +- [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) diff --git a/errors/template.md b/errors/template.md new file mode 100644 index 0000000000000..711da0c52abf2 --- /dev/null +++ b/errors/template.md @@ -0,0 +1,13 @@ +# + +#### Why This Error Occurred + + + +#### Possible Ways to Fix It + + + +### Useful Links + + diff --git a/errors/undefined-webpack-config.md b/errors/undefined-webpack-config.md new file mode 100644 index 0000000000000..fb15fdb731ba9 --- /dev/null +++ b/errors/undefined-webpack-config.md @@ -0,0 +1,28 @@ +# Missing webpack config + +#### Why This Error Occurred + +The value returned from the custom `webpack` function in your `next.config.js` was undefined. This can occur from the initial config value not being returned. + +#### Possible Ways to Fix It + +Make sure to return the `webpack` config from your custom `webpack` function in your `next.config.js` + +```js +// next.config.js + +module.exports = { + webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => { + // Note: we provide webpack above so you should not `require` it + // Perform customizations to webpack config + config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//)) + + // Important: return the modified config + return config + }, +} +``` + +### Useful Links + +- [Custom webpack config Documentation](https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config) diff --git a/errors/url-deprecated.md b/errors/url-deprecated.md index d583128789469..df352d8e07068 100644 --- a/errors/url-deprecated.md +++ b/errors/url-deprecated.md @@ -10,9 +10,9 @@ The reason this is going away is that we want to make things very predictable an #### Possible Ways to Fix It -https://github.com/zeit/next-codemod#url-to-withrouter +https://nextjs.org/docs/advanced-features/codemods#url-to-withrouter -Since Next 5 we provide a way to explicitly inject the Next.js router object into pages and all their decending components. +Since Next 5 we provide a way to explicitly inject the Next.js router object into pages and all their descending components. The `router` property that is injected will hold the same values as `url`, like `pathname`, `asPath`, and `query`. Here's an example of using `withRouter`: @@ -33,4 +33,4 @@ export default withRouter(Page) We provide a codemod (a code to code transformation) to automatically change the `url` property usages to `withRouter`. -You can find this codemod and instructions on how to run it here: https://github.com/zeit/next-codemod#url-to-withrouter +You can find this codemod and instructions on how to run it here: https://nextjs.org/docs/advanced-features/codemods#url-to-withrouter diff --git a/errors/webpack5.md b/errors/webpack5.md new file mode 100644 index 0000000000000..b8c994c549e3a --- /dev/null +++ b/errors/webpack5.md @@ -0,0 +1,45 @@ +# Webpack 5 Adoption + +#### Why This Message Occurred + +Next.js has adopted webpack 5 as the default for compilation. We've spent a lot of effort into ensuring the transition from webpack 4 to 5 will be as smooth as possible. For example Next.js now comes with both webpack 4 and 5 allowing you to adopt webpack 5 by adding a flag to your `next.config.js`: + +```js +module.exports = { + // Webpack 5 is enabled by default + // You can still use webpack 4 while upgrading to the latest version of Next.js by adding the "webpack5: false" flag + webpack5: false, +} +``` + +Using webpack 5 in your application has many benefits, notably: + +- Improved Disk Caching: `next build` is significantly faster on subsequent builds +- Improved Fast Refresh: Fast Refresh work is prioritized +- Improved Long Term Caching of Assets: Deterministic code output that is less likely to change between builds +- Improved Tree Shaking +- Support for assets using `new URL("file.png", import.meta.url)` +- Support for web workers using `new Worker(new URL("worker.js", import.meta.url))` +- Support for `exports`/`imports` field in `package.json` + +In the past releases we have gradually rolled out webpack 5 to Next.js applications: + +- In Next.js 10.2 we automatically opted-in applications without custom webpack configuration in `next.config.js` +- In Next.js 10.2 we automatically opted-in applications that do not have a `next.config.js` +- In Next.js 11 webpack 5 was enabled by default for all applications. You can still opt-out and use webpack 4 to help with backwards compatibility using `webpack5: false` in `next.config.js` + +In the next major version webpack 4 support will be removed. + +#### Custom webpack configuration + +In case you do have custom webpack configuration, either through custom plugins or your own modifications you'll have to take a few steps to ensure your applications works with webpack 5. + +- When using `next-transpile-modules` make sure you use the latest version which includes [this patch](https://github.com/martpie/next-transpile-modules/pull/179) +- When using `@zeit/next-css` / `@zeit/next-sass` make sure you use the [built-in CSS/Sass support](https://nextjs.org/docs/basic-features/built-in-css-support) instead +- When using `@zeit/next-preact` use [this example](https://github.com/vercel/next-plugins/tree/master/packages/next-preact) instead +- When using `@zeit/next-source-maps` use the [built-in production Source Map support](https://nextjs.org/docs/advanced-features/source-maps) +- When using webpack plugins make sure they're upgraded to the latest version, in most cases the latest version will include webpack 5 support. In some cases these upgraded webpack plugins will only support webpack 5. + +### Useful Links + +In case you're running into issues you can connect with the community in [this help discussion](https://github.com/vercel/next.js/discussions/23498). diff --git a/examples/active-class-name/.gitignore b/examples/active-class-name/.gitignore new file mode 100644 index 0000000000000..1437c53f70bc2 --- /dev/null +++ b/examples/active-class-name/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/examples/active-class-name/README.md b/examples/active-class-name/README.md index 891665503f7d2..902d79e24b7fe 100644 --- a/examples/active-class-name/README.md +++ b/examples/active-class-name/README.md @@ -2,41 +2,26 @@ ReactRouter has a convenience property on the `Link` element to allow an author to set the _active_ className on a link. This example replicates that functionality using Next's own `Link`. +## Preview + +Preview the example live on [StackBlitz](http://stackblitz.com/): + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/active-class-name) + ## Deploy your own -Deploy the example using [Vercel](https://vercel.com): +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/active-class-name) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/active-class-name&project-name=active-class-name&repository-name=active-class-name) ## How to use -### Using `create-next-app` - Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: ```bash -npm init next-app --example active-class-name active-class-name-app +npx create-next-app --example active-class-name active-class-name-app # or yarn create next-app --example active-class-name active-class-name-app ``` -### Download manually - -Download the example: - -```bash -curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/active-class-name -cd active-class-name -``` - -Install it and run: - -```bash -npm install -npm run dev -# or -yarn -yarn dev -``` - -Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/active-class-name/components/ActiveLink.js b/examples/active-class-name/components/ActiveLink.js index 2c561338a36a2..0db59b6a9227a 100644 --- a/examples/active-class-name/components/ActiveLink.js +++ b/examples/active-class-name/components/ActiveLink.js @@ -8,8 +8,11 @@ const ActiveLink = ({ children, activeClassName, ...props }) => { const child = Children.only(children) const childClassName = child.props.className || '' + // pages/index.js will be matched via props.href + // pages/about.js will be matched via props.href + // pages/[slug].js will be matched via props.as const className = - asPath === props.href + asPath === props.href || asPath === props.as ? `${childClassName} ${activeClassName}`.trim() : childClassName diff --git a/examples/active-class-name/components/Nav.js b/examples/active-class-name/components/Nav.js index 32e15a23466c2..2c710bb42468f 100644 --- a/examples/active-class-name/components/Nav.js +++ b/examples/active-class-name/components/Nav.js @@ -22,6 +22,11 @@ const Nav = () => (
    About +
  • + + Dynamic Route + +
  • ) diff --git a/examples/active-class-name/package.json b/examples/active-class-name/package.json index feb47ede884f6..6513b825ee0a6 100644 --- a/examples/active-class-name/package.json +++ b/examples/active-class-name/package.json @@ -1,16 +1,14 @@ { - "name": "active-class-name", - "version": "1.0.0", + "private": true, "scripts": { "dev": "next", "build": "next build", "start": "next start" }, - "author": "Remy Sharp ", "dependencies": { "next": "latest", - "react": "^16.7.0", - "react-dom": "^16.7.0" - }, - "license": "ISC" + "react": "^17.0.2", + "react-dom": "^17.0.2", + "prop-types": "^15.7.2" + } } diff --git a/examples/active-class-name/pages/[slug].js b/examples/active-class-name/pages/[slug].js new file mode 100644 index 0000000000000..0f8f73e5888d0 --- /dev/null +++ b/examples/active-class-name/pages/[slug].js @@ -0,0 +1,14 @@ +import { useRouter } from 'next/router' +import Nav from '../components/Nav' + +const SlugPage = () => { + const { asPath } = useRouter() + return ( + <> +