diff --git a/.config/nextest.toml b/.config/nextest.toml index f16e9507d35ba..0cf785bf47865 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -5,5 +5,5 @@ filter = "package(next-dev-tests)" leak-timeout = "500ms" retries = 2 slow-timeout = "60s" -threads-required = 2 +threads-required = 4 failure-output = "immediate-final" \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 656d628179b73..ad213929292a9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,34 +1,11 @@ # Learn how to add code owners here: # https://help.github.com/en/articles/about-code-owners -* @timneutkens @ijjk @shuding @huozhi @feedthejim -/.git* @vercel/next-js -/docs/ @vercel/next-js @leerob -/errors/ @vercel/next-js @leerob -/examples/ @vercel/next-js @leerob @steven-tey -/scripts/ @vercel/next-js -/.alex* @vercel/next-js @leerob -/.eslint* @vercel/next-js @leerob -/.prettier* @vercel/next-js @leerob -/*.md @vercel/next-js @leerob -/packages/create-next-app/ @vercel/next-js -/pnpm-lock.yaml @vercel/next-js @vercel/web-tooling +# Codeowners now use Vercel Spaces +# Search .vercel.approvers for all files # Image Component (@styfle) -/**/*image* @timneutkens @ijjk @shuding @styfle @huozhi -/**/*image*/** @timneutkens @ijjk @shuding @styfle @huozhi -/packages/next/client/use-intersection.tsx @timneutkens @ijjk @shuding @styfle -/packages/next/server/lib/squoosh/ @timneutkens @ijjk @shuding @styfle -/packages/next/server/serve-static.ts @timneutkens @ijjk @shuding @styfle @huozhi -/packages/next/server/config.ts @timneutkens @ijjk @shuding @styfle @huozhi +/**/*image* @timneutkens @ijjk @shuding @styfle @huozhi @vercel/devex +/**/*image*/** @timneutkens @ijjk @shuding @styfle @huozhi @vercel/devex -# Tooling & Telemetry - -/packages/next/src/build/ @timneutkens @ijjk @shuding @vercel/web-tooling @huozhi -/packages/next/src/telemetry/ @timneutkens @ijjk @shuding @padmaia -/packages/next-swc/ @timneutkens @ijjk @shuding @vercel/web-tooling -Cargo.toml @timneutkens @ijjk @shuding @vercel/web-tooling -Cargo.lock @timneutkens @ijjk @shuding @vercel/web-tooling -/.cargo/config.toml @timneutkens @ijjk @shuding @vercel/web-tooling -/.config/nextest.toml @timneutkens @ijjk @shuding @vercel/web-tooling \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/4.docs_request.yml b/.github/ISSUE_TEMPLATE/4.docs_request.yml index a2fe06929844e..67111ee7e776d 100644 --- a/.github/ISSUE_TEMPLATE/4.docs_request.yml +++ b/.github/ISSUE_TEMPLATE/4.docs_request.yml @@ -4,6 +4,12 @@ title: 'Docs: ' labels: - 'template: documentation' body: + - type: markdown + attributes: + value: Before opening this issue to request a docs improvement, is this something you can help us with? Your contributions are welcomed and appreciated. See our [Docs Contribution Guide](https://nextjs.org/docs/community/contribution-guide) to learn how to edit the Next.js docs. + - type: markdown + attributes: + value: Thank you for helping us improve the docs! - type: textarea attributes: label: What is the improvement or update you wish to see? diff --git a/.github/actions/next-stats-action/Dockerfile b/.github/actions/next-stats-action/Dockerfile index 61492813942db..d61c0eedb1091 100644 --- a/.github/actions/next-stats-action/Dockerfile +++ b/.github/actions/next-stats-action/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16-bullseye +FROM ubuntu:22.04 LABEL com.github.actions.name="Next.js PR Stats" LABEL com.github.actions.description="Compares stats of a PR with the main branch" @@ -6,8 +6,15 @@ LABEL repository="https://github.com/vercel/next-stats-action" COPY . /next-stats +RUN apt update && apt upgrade -y +RUN apt install unzip wget curl nano htop screen build-essential pkg-config libssl-dev git build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libreadline-dev libffi-dev python3 moreutils jq iproute2 openssh-server sudo whois dnsutils -y + +RUN ln $(which python3) /usr/bin/python + +RUN curl -sfLS https://install-node.vercel.app/v18 | bash -s -- -f + # Install node_modules -RUN npm i -g pnpm@7.24.3 +RUN npm i -g pnpm@7.24.3 yarn@1.22.19 RUN cd /next-stats && pnpm install --production RUN git config --global user.email 'stats@localhost' diff --git a/.github/actions/next-stats-action/src/constants.js b/.github/actions/next-stats-action/src/constants.js index 7d199d9dd1083..66eff2c821155 100644 --- a/.github/actions/next-stats-action/src/constants.js +++ b/.github/actions/next-stats-action/src/constants.js @@ -8,9 +8,6 @@ const mainRepoDir = path.join(workDir, 'main-repo') const diffRepoDir = path.join(workDir, 'diff-repo') 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', @@ -25,6 +22,5 @@ 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 ce7bdd50f91a6..a7d9fc4a3e113 100644 --- a/.github/actions/next-stats-action/src/index.js +++ b/.github/actions/next-stats-action/src/index.js @@ -64,22 +64,20 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { statsConfig.mainRepo = actionInfo.prRepo } - // clone main repository/ref - if (!actionInfo.skipClone) { - await cloneRepo(statsConfig.mainRepo, mainRepoDir, statsConfig.mainBranch) - } /* eslint-disable-next-line */ actionInfo.commitId = await getCommitId(diffRepoDir) let mainNextSwcVersion if (!actionInfo.skipClone) { + let mainRef = statsConfig.mainBranch + if (actionInfo.isRelease) { - logger('Release detected, resetting mainRepo to last stable tag') - const lastStableTag = await getLastStable(mainRepoDir, actionInfo.prRef) + logger('Release detected, using last stable tag') + const lastStableTag = await getLastStable(diffRepoDir, actionInfo.prRef) + mainRef = lastStableTag mainNextSwcVersion = lastStableTag if (!lastStableTag) throw new Error('failed to get last stable tag') console.log('using latestStable', lastStableTag) - await checkoutRef(lastStableTag, mainRepoDir) /* eslint-disable-next-line */ actionInfo.lastStableTag = lastStableTag @@ -90,12 +88,15 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { /* eslint-disable-next-line */ actionInfo.commentEndpoint = `https://api.github.com/repos/${statsConfig.mainRepo}/commits/${actionInfo.commitId}/comments` } - } else if (statsConfig.autoMergeMain) { + } + + await cloneRepo(statsConfig.mainRepo, mainRepoDir, mainRef) + + if (!actionInfo.isRelease && statsConfig.autoMergeMain) { logger('Attempting auto merge of main branch') await mergeBranch(statsConfig.mainBranch, mainRepoDir, diffRepoDir) } } - let mainRepoPkgPaths let diffRepoPkgPaths @@ -132,6 +133,9 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { ) .catch(console.error) + console.log(await exec(`ls ${path.join(__dirname, '../native')}`)) + console.log(await exec(`cd ${dir} && ls ${dir}/packages/next-swc/native`)) + logger(`Linking packages in ${dir}`) const isMainRepo = dir === mainRepoDir const pkgPaths = await linkPackages({ 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 83aa1deea8005..1dbfc3f785919 100644 --- a/.github/actions/next-stats-action/src/prepare/action-info.js +++ b/.github/actions/next-stats-action/src/prepare/action-info.js @@ -61,6 +61,10 @@ module.exports = function actionInfo() { (GITHUB_REF || '').includes('canary'), } + if (info.isRelease) { + info.prRef = 'canary' + } + // get comment if (GITHUB_EVENT_PATH) { const event = require(GITHUB_EVENT_PATH) 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 4b971162bc88c..5edf6865d387a 100644 --- a/.github/actions/next-stats-action/src/prepare/repo-setup.js +++ b/.github/actions/next-stats-action/src/prepare/repo-setup.js @@ -3,7 +3,6 @@ const fs = require('fs-extra') const exec = require('../util/exec') const { remove } = require('fs-extra') const logger = require('../util/logger') -const semver = require('semver') const execa = require('execa') module.exports = (actionInfo) => { @@ -14,21 +13,16 @@ module.exports = (actionInfo) => { `git clone ${actionInfo.gitRoot}${repoPath} --single-branch --branch ${branch} --depth=${depth} ${dest}` ) }, - async getLastStable(repoDir = '', ref) { - const { stdout } = await exec(`cd ${repoDir} && git tag -l`) - const tags = stdout.trim().split('\n') - let lastStableTag + async getLastStable(repoDir = '') { + const { stdout } = await exec(`cd ${repoDir} && git describe`) + const tag = stdout.trim() - for (let i = tags.length - 1; i >= 0; i--) { - const curTag = tags[i] - // stable doesn't include `-canary` or `-beta` - if (!curTag.includes('-') && !ref.includes(curTag)) { - if (!lastStableTag || semver.gt(curTag, lastStableTag)) { - lastStableTag = curTag - } - } + if (!tag || !tag.startsWith('v')) { + throw new Error(`Failed to get tag info ${stdout}`) } - return lastStableTag + const tagParts = tag.split('-canary')[0].split('.') + // last stable tag will always be 1 patch less than canary + return `${tagParts[0]}.${tagParts[1]}.${Number(tagParts[2]) - 1}` }, async getCommitId(repoDir = '') { const { stdout } = await exec(`cd ${repoDir} && git rev-parse HEAD`) @@ -79,6 +73,7 @@ module.exports = (actionInfo) => { } const pkgData = require(pkgDataPath) const { name } = pkgData + pkgDatas.set(name, { pkgDataPath, pkg, @@ -103,7 +98,7 @@ module.exports = (actionInfo) => { if (!pkgData.files) { pkgData.files = [] } - pkgData.files.push('native/*') + pkgData.files.push('native') require('console').log( 'using swc binaries: ', await exec(`ls ${path.join(path.dirname(pkgDataPath), 'native')}`) @@ -124,18 +119,11 @@ module.exports = (actionInfo) => { pkgData.dependencies['@next/swc'] = pkgDatas.get('@next/swc').packedPkgPath } else { - pkgData.files.push('native/*') + pkgData.files.push('native') } } } - if (pkgData?.scripts?.prepublishOnly) { - // There's a bug in `pnpm pack` where it will run - // the prepublishOnly script and that will fail. - // See https://github.com/pnpm/pnpm/issues/2941 - delete pkgData.scripts.prepublishOnly - } - await fs.writeFile( pkgDataPath, JSON.stringify(pkgData, null, 2), @@ -147,21 +135,17 @@ module.exports = (actionInfo) => { // to the correct versions await Promise.all( Array.from(pkgDatas.keys()).map(async (pkgName) => { - const { pkg, pkgPath, pkgData, packedPkgPath } = pkgDatas.get(pkgName) - // Copied from pnpm source: https://github.com/pnpm/pnpm/blob/5a5512f14c47f4778b8d2b6d957fb12c7ef40127/releasing/plugin-commands-publishing/src/pack.ts#L96 - const tmpTarball = path.join( - pkgPath, - `${pkgData.name.replace('@', '').replace('/', '-')}-${ - pkgData.version - }.tgz` - ) - await execa('pnpm', ['pack'], { + const { pkgPath, packedPkgPath } = pkgDatas.get(pkgName) + + await execa('yarn', ['pack', '-f', packedPkgPath], { cwd: pkgPath, + env: { + ...process.env, + COREPACK_ENABLE_STRICT: '0', + }, }) - await fs.copyFile(tmpTarball, packedPkgPath) }) ) - 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 2f0e053c35c78..afc647d399be5 100644 --- a/.github/actions/next-stats-action/src/run/index.js +++ b/.github/actions/next-stats-action/src/run/index.js @@ -1,13 +1,12 @@ const path = require('path') const fs = require('fs-extra') -const getPort = require('get-port') const glob = require('../util/glob') const exec = require('../util/exec') const logger = require('../util/logger') const getDirSize = require('./get-dir-size') const collectStats = require('./collect-stats') const collectDiffs = require('./collect-diffs') -const { statsAppDir, diffRepoDir, yarnEnvValues } = require('../constants') +const { statsAppDir, diffRepoDir } = require('../constants') async function runConfigs( configs = [], @@ -59,13 +58,7 @@ async function runConfigs( const buildStart = Date.now() console.log( - await exec( - `cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, - false, - { - env: yarnEnvValues, - } - ) + await exec(`cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, false) ) curStats.General.buildDuration = Date.now() - buildStart @@ -160,13 +153,7 @@ async function runConfigs( const secondBuildStart = Date.now() console.log( - await exec( - `cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, - false, - { - env: yarnEnvValues, - } - ) + await exec(`cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, false) ) curStats.General.buildDurationCached = Date.now() - secondBuildStart } @@ -203,13 +190,9 @@ async function linkPkgs(pkgDir = '', pkgPaths) { } await fs.writeFile(pkgJsonPath, JSON.stringify(pkgData, null, 2), 'utf8') - await fs.remove(yarnEnvValues.YARN_CACHE_FOLDER) await exec( `cd ${pkgDir} && pnpm install --strict-peer-dependencies=false`, - false, - { - env: yarnEnvValues, - } + false ) } diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml new file mode 100644 index 0000000000000..bb6044cbf08f1 --- /dev/null +++ b/.github/actions/setup-rust/action.yml @@ -0,0 +1,65 @@ +name: 'Rust Setup' +description: 'Sets up the Rust toolchain for CI' +inputs: + targets: + description: 'Comma-separated list of target triples to install for this toolchain' + required: false + components: + description: 'Comma-separated list of components to be additionally installed' + required: false + skip-install: + description: 'Sets environment variables without installing the rust toolchain' + required: false + +runs: + using: 'composite' + steps: + - name: 'Get toolchain version from file' + id: file + shell: bash + run: echo "toolchain=$(cat ./rust-toolchain)" >> $GITHUB_OUTPUT + + - shell: bash + run: | + : force toolchain version + echo "RUST_TOOLCHAIN=${{ steps.file.outputs.toolchain }}" >> $GITHUB_ENV + + - shell: bash + run: | + : disable incremental compilation + if [ -z "${CARGO_INCREMENTAL+set}" ]; then + echo CARGO_INCREMENTAL=0 >> $GITHUB_ENV + fi + + - shell: bash + run: | + : enable colors in Cargo output + if [ -z "${CARGO_TERM_COLOR+set}" ]; then + echo CARGO_TERM_COLOR=always >> $GITHUB_ENV + fi + + - shell: bash + run: | + : enable rust backtrace + if [ -z "${RUST_BACKTRACE+set}" ]; then + echo RUST_BACKTRACE=short >> $GITHUB_ENV + fi + + - shell: bash + run: | + : enable faster cargo sparse registry + if [ -z "${CARGO_REGISTRIES_CRATES_IO_PROTOCOL+set}" ]; then + echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse >> $GITHUB_ENV + fi + + - name: 'Setup Rust toolchain' + uses: dtolnay/rust-toolchain@master + if: ${{ !inputs.skip-install }} + with: + toolchain: ${{ steps.file.outputs.toolchain }} + targets: ${{ inputs.targets }} + components: ${{ inputs.components }} + + - name: 'Add cargo problem matchers' + shell: bash + run: echo "::add-matcher::${{ github.action_path }}/matchers.json" diff --git a/.github/actions/setup-rust/matchers.json b/.github/actions/setup-rust/matchers.json new file mode 100644 index 0000000000000..aff246556a3db --- /dev/null +++ b/.github/actions/setup-rust/matchers.json @@ -0,0 +1,44 @@ +{ + "problemMatcher": [ + { + "owner": "cargo-common", + "pattern": [ + { + "regexp": "^(warning|warn|error)(?:\\[(\\S*)\\])?: (.*)$", + "severity": 1, + "code": 2, + "message": 3 + }, + { + "regexp": "^(?:[\\s->=]*(.*):(\\d*):(\\d*)|.*)$", + "file": 1, + "line": 2, + "column": 3 + } + ] + }, + { + "owner": "cargo-test", + "pattern": [ + { + "regexp": "^.*panicked\\s+at\\s+'(.*)',\\s+(.*):(\\d+):(\\d+)$", + "message": 1, + "file": 2, + "line": 3, + "column": 4 + } + ] + }, + { + "owner": "rustfmt", + "pattern": [ + { + "regexp": "^(Diff in (\\S+)) at line (\\d+):", + "message": 1, + "file": 2, + "line": 3 + } + ] + } + ] +} diff --git a/.github/labeler.json b/.github/labeler.json index d9c2247124890..4ce7736dd6498 100644 --- a/.github/labeler.json +++ b/.github/labeler.json @@ -26,7 +26,6 @@ { "type": "user", "pattern": "JanKaifer" }, { "type": "user", "pattern": "javivelasco" }, { "type": "user", "pattern": "kikobeats" }, - { "type": "user", "pattern": "leerob" }, { "type": "user", "pattern": "schniz" }, { "type": "user", "pattern": "sebmarkbage" }, { "type": "user", "pattern": "shuding" }, @@ -34,10 +33,18 @@ { "type": "user", "pattern": "timneutkens" }, { "type": "user", "pattern": "wyattjoh" } ], - "created-by: Next.js docs team": [ + "created-by: Next.js DevEx team": [ + { "type": "user", "pattern": "leerob" }, { "type": "user", "pattern": "ismaelrumzan" }, - { "type": "user", "pattern": "MaedahBatool" }, - { "type": "user", "pattern": "molebox" } + { "type": "user", "pattern": "molebox" }, + { "type": "user", "pattern": "delbaoliveira" }, + { "type": "user", "pattern": "lydiahallie" }, + { "type": "user", "pattern": "steven-tey" }, + { "type": "user", "pattern": "nutlope" }, + { "type": "user", "pattern": "stephdietz" }, + { "type": "user", "pattern": "timeyoutakeit" }, + { "type": "user", "pattern": "s3prototype" }, + { "type": "user", "pattern": "manovotny" } ], "created-by: web-tooling team": [ { "type": "user", "pattern": "alexkirsz" }, diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b898a902797e1..e7a876f2bdcb1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,12 @@ Choose the right checklist for the change(s) that you're making: ## For Contributors -### Improving Documentation or adding/fixing Examples +### Improving Documentation + +- Run `pnpm prettier-fix` to fix formatting issues before opening the PR. +- Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide + +### Adding or Updating Examples - The "examples guidelines" are followed from our contributing doc https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md - Make sure the linting passes by running `pnpm build && pnpm lint`. See https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md @@ -25,7 +30,6 @@ Choose the right checklist for the change(s) that you're making: - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md - ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 8216e812908ad..012c4164bdbc8 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -8,7 +8,6 @@ on: env: NAPI_CLI_VERSION: 2.14.7 TURBO_VERSION: 1.9.6 - RUST_TOOLCHAIN: nightly-2023-03-09 PNPM_VERSION: 7.24.3 NODE_MAINTENANCE_VERSION: 16 NODE_LTS_VERSION: 18 @@ -23,16 +22,10 @@ jobs: NEXT_SKIP_NATIVE_POSTINSTALL: 1 TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} outputs: - docsChange: ${{ steps.docs-change.outputs.DOCS_CHANGE }} - codemodChange: ${{ steps.codemod-change.outputs.CODEMOD_CHANGE }} isRelease: ${{ github.event_name != 'workflow_dispatch' && steps.check-release.outputs.IS_RELEASE }} - swcChange: ${{ steps.swc-change.outputs.SWC_CHANGE }} - turboToken: ${{ steps.turbo-token.outputs.TURBO_TOKEN }} - weekNum: ${{ steps.get-week.outputs.WEEK }} steps: - name: Setup node uses: actions/setup-node@v3 - if: ${{ steps.docs-change.outputs.docsChange == 'nope' }} with: node-version: ${{ env.NODE_LTS_VERSION }} check-latest: true @@ -98,7 +91,7 @@ jobs: - host: windows-latest build: | npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" - turbo run build-native-no-plugin -- --release --target i686-pc-windows-msvc + turbo run build-native-no-plugin --summarize -- --release --target i686-pc-windows-msvc target: 'i686-pc-windows-msvc' - host: ubuntu-latest target: 'x86_64-unknown-linux-gnu' @@ -165,7 +158,7 @@ jobs: target: 'aarch64-pc-windows-msvc' build: | npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" - turbo run build-native-no-plugin-woa-release -- --target aarch64-pc-windows-msvc + turbo run build-native-no-plugin-woa-release --summarize -- --target aarch64-pc-windows-msvc needs: build name: stable - ${{ matrix.settings.target }} - node@16 runs-on: ${{ matrix.settings.host }} @@ -198,14 +191,11 @@ jobs: node-version: ${{ env.NODE_LTS_VERSION }} check-latest: true - - name: Install - uses: actions-rs/toolchain@v1 - if: ${{ !matrix.settings.docker }} + - name: Install Rust + uses: ./.github/actions/setup-rust with: - profile: minimal - override: true - toolchain: ${{ env.RUST_TOOLCHAIN }} - target: ${{ matrix.settings.target }} + targets: ${{ matrix.settings.target }} + skip-install: ${{ matrix.settings.docker }} - name: Cache cargo registry uses: actions/cache@v3 @@ -234,7 +224,21 @@ jobs: if: ${{ matrix.settings.docker }} with: image: ${{ matrix.settings.docker }} - options: -e RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} -e NAPI_CLI_VERSION=${{ env.NAPI_CLI_VERSION }} -e TURBO_VERSION=${{ env.TURBO_VERSION }} -e TURBO_TEAM=vercel -e TURBO_TOKEN=${{ secrets.TURBO_TOKEN }} -e TURBO_REMOTE_ONLY=true -v ${{ env.HOME }}/.cargo/git:/root/.cargo/git -v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry -v ${{ github.workspace }}:/build -w /build + options: >- + -e RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + -e CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }} + -e CARGO_TERM_COLOR=${{ env.CARGO_TERM_COLOR }} + -e RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} + -e CARGO_REGISTRIES_CRATES_IO_PROTOCOL=${{ env.CARGO_REGISTRIES_CRATES_IO_PROTOCOL }} + -e NAPI_CLI_VERSION=${{ env.NAPI_CLI_VERSION }} + -e TURBO_VERSION=${{ env.TURBO_VERSION }} + -e TURBO_TEAM=vercel + -e TURBO_TOKEN=${{ secrets.TURBO_TOKEN }} + -e TURBO_REMOTE_ONLY=true + -v ${{ env.HOME }}/.cargo/git:/root/.cargo/git + -v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry + -v ${{ github.workspace }}:/build + -w /build run: ${{ matrix.settings.build }} - name: 'Build' @@ -242,13 +246,20 @@ jobs: if: ${{ !matrix.settings.docker }} shell: bash + - name: 'check build cache status' + id: check-did-build + shell: bash + run: if [[ ! -z $(ls .turbo/runs) ]]; then echo "DID_BUILD=yup" >> $GITHUB_OUTPUT; fi + # Trying to upload metrics for the Turbopack to datadog's CI pipeline execution - - name: 'Upload turbopack build metrics' + - name: 'Collect turbopack build metrics' + id: check-turbopack-bytesize shell: bash - if: ${{ needs.build.outputs.isRelease == 'true' }} + if: ${{ steps.check-did-build.outputs.DID_BUILD == 'yup' }} continue-on-error: true run: | - npm install -g @datadog/datadog-ci + mkdir -p ./turbopack-bin-size + shopt -s nullglob for filename in packages/next-swc/native/next-swc.*.node; do # Strip out filename to extract target triple export FILENAME=$(basename ${filename}) @@ -256,9 +267,16 @@ jobs: export FILENAME=${FILENAME%.node} export BYTESIZE=$(wc -c < $filename | xargs) echo "Reporting $FILENAME:$BYTESIZE for Turbopack bytesize" - datadog-ci metric --no-fail --level pipeline --metrics "turbopack.bytesize.$FILENAME:$BYTESIZE" + echo "turbopack.bytesize.$FILENAME:$BYTESIZE" > ./turbopack-bin-size/${{ matrix.settings.target }} done + - name: Upload turbopack bytesize artifact + if: ${{ steps.check-did-build.outputs.DID_BUILD == 'yup' }} + uses: actions/upload-artifact@v3 + with: + name: turbopack-bytesize + path: turbopack-bin-size/* + - name: Upload swc artifact if: ${{ needs.build.outputs.isRelease == 'true' }} uses: actions/upload-artifact@v3 @@ -292,12 +310,9 @@ jobs: check-latest: true - name: Install Rust - uses: actions-rs/toolchain@v1 + uses: ./.github/actions/setup-rust with: - profile: minimal - toolchain: ${{ env.RUST_TOOLCHAIN }} - override: true - target: wasm32-unknown-unknown + targets: wasm32-unknown-unknown - run: npm i -g turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} @@ -341,7 +356,6 @@ jobs: steps: - name: Setup node uses: actions/setup-node@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} with: node-version: ${{ env.NODE_LTS_VERSION }} check-latest: true @@ -367,7 +381,7 @@ jobs: name: wasm-binaries path: packages/next-swc/crates/wasm - - run: npm i -g npm@9 # need latest version for provenance + - run: npm i -g npm@9.6.7 # need latest version for provenance (pinning to avoid bugs) - run: npm i -g pnpm@${PNPM_VERSION} - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc - run: ./scripts/publish-native.js @@ -462,3 +476,25 @@ jobs: - uses: ./.github/actions/next-stats-action env: PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} + + upload_turbopack_bytesize: + name: Upload Turbopack Bytesize trace to Datadog + runs-on: ubuntu-latest + needs: [build-native] + env: + DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + steps: + - name: Collect bytesize traces + uses: actions/download-artifact@v3 + with: + name: turbopack-bytesize + path: turbopack-bin-size + - name: Upload to Datadog + run: | + ls -al turbopack-bin-size + npm install -g @datadog/datadog-ci + for filename in turbopack-bin-size/*; do + export BYTESIZE+=" --metrics $(cat $filename)" + done + echo "Reporting $BYTESIZE" + datadog-ci metric --no-fail --level pipeline $BYTESIZE diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 8c7e0e4f0fb1d..7fb1db8eb03e5 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -9,11 +9,12 @@ on: env: NAPI_CLI_VERSION: 2.14.7 TURBO_VERSION: 1.9.6 - RUST_TOOLCHAIN: nightly-2023-03-09 PNPM_VERSION: 7.24.3 NODE_MAINTENANCE_VERSION: 16 NODE_LTS_VERSION: 18 TEST_CONCURRENCY: 6 + # disable backtrace for test snapshots + RUST_BACKTRACE: 0 TURBO_TEAM: 'vercel' TURBO_REMOTE_ONLY: 'true' @@ -21,18 +22,30 @@ env: # we build a dev binary for use in CI so skip downloading # canary next-swc binaries in the monorepo NEXT_SKIP_NATIVE_POSTINSTALL: 1 + DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + DATADOG_TRACE_NEXTJS_TEST: 'true' + DD_ENV: 'ci' TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} NEXT_TEST_JOB: 1 jobs: - build: - name: build + build-native: + name: build-native uses: ./.github/workflows/build_reusable.yml + with: + skipInstallBuild: 'yes' + secrets: inherit + + build-next: + name: build-next + uses: ./.github/workflows/build_reusable.yml + with: + skipNativeBuild: 'yes' secrets: inherit lint: name: lint - needs: ['build'] + needs: ['build-native', 'build-next'] uses: ./.github/workflows/build_reusable.yml with: @@ -41,7 +54,7 @@ jobs: check-types-precompiled: name: types and precompiled - needs: ['build'] + needs: ['build-native', 'build-next'] uses: ./.github/workflows/build_reusable.yml with: @@ -51,29 +64,56 @@ jobs: test-cargo-unit: name: test cargo unit - needs: ['build'] + needs: ['build-next'] uses: ./.github/workflows/build_reusable.yml with: skipForDocsOnly: 'yes' + needsRust: 'yes' skipInstallBuild: 'yes' + skipNativeBuild: 'yes' afterBuild: turbo run test-cargo-unit secrets: inherit + test-cargo-integration: + name: test cargo integration + needs: ['build-next'] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + needsNextest: 'yes' + needsRust: 'yes' + skipNativeBuild: 'yes' + afterBuild: xvfb-run turbo run test-cargo-integration + + test-cargo-bench: + name: test cargo benchmarks + needs: ['build-next'] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + needsRust: 'yes' + skipNativeBuild: 'yes' + afterBuild: xvfb-run turbo run test-cargo-bench + rust-check: name: rust check - needs: ['build'] + needs: ['build-next'] uses: ./.github/workflows/build_reusable.yml with: skipForDocsOnly: 'yes' + needsRust: 'yes' skipInstallBuild: 'yes' + skipNativeBuild: 'yes' afterBuild: turbo run rust-check secrets: inherit test-turbopack-dev: name: test turbopack dev - needs: ['build'] + needs: ['build-native', 'build-next'] uses: ./.github/workflows/build_reusable.yml with: skipForDocsOnly: 'yes' @@ -82,8 +122,7 @@ jobs: test-next-swc-wasm: name: test next-swc wasm - needs: ['build'] - + needs: ['build-native', 'build-next'] uses: ./.github/workflows/build_reusable.yml with: skipForDocsOnly: 'yes' @@ -92,7 +131,7 @@ jobs: test-dev: name: test dev - needs: ['build'] + needs: ['build-native', 'build-next'] strategy: fail-fast: false matrix: @@ -106,7 +145,7 @@ jobs: test-prod: name: test prod - needs: ['build'] + needs: ['build-native', 'build-next'] strategy: fail-fast: false matrix: @@ -120,7 +159,7 @@ jobs: test-integration: name: test integration - needs: ['build'] + needs: ['build-native', 'build-next'] strategy: fail-fast: false matrix: @@ -135,7 +174,7 @@ jobs: test-firefox-safari: name: test firefox and safari - needs: ['build'] + needs: ['build-native', 'build-next'] uses: ./.github/workflows/build_reusable.yml with: @@ -143,16 +182,27 @@ jobs: afterBuild: pnpm playwright install && BROWSER_NAME=firefox node run-tests.js test/integration/production/test/index.test.js && BROWSER_NAME=safari NEXT_TEST_MODE=start node run-tests.js -c 1 test/integration/production/test/index.test.js test/e2e/basepath.test.ts && BROWSER_NAME=safari DEVICE_NAME='iPhone XR' node run-tests.js -c 1 test/production/prerender-prefetch/index.test.ts secrets: inherit + report-test-results: + needs: ['test-dev', 'test-prod', 'test-integration', 'test-turbopack-dev'] + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + uploadTestTrace: 'yes' + secrets: inherit + tests-pass: needs: [ - 'build', + 'build-native', + 'build-next', 'lint', 'check-types-precompiled', 'test-dev', 'test-prod', 'test-integration', 'test-cargo-unit', + 'test-cargo-integration', + 'test-cargo-bench', 'rust-check', 'test-next-swc-wasm', 'test-turbopack-dev', diff --git a/.github/workflows/build_reusable.yml b/.github/workflows/build_reusable.yml index 666582d176585..8964d728fb456 100644 --- a/.github/workflows/build_reusable.yml +++ b/.github/workflows/build_reusable.yml @@ -11,6 +11,10 @@ on: required: false description: 'whether to skip pnpm install && pnpm build' type: string + skipNativeBuild: + required: false + description: 'whether to skip building native modules' + type: string skipForDocsOnly: required: false description: 'skip for docs only changes' @@ -19,6 +23,10 @@ on: required: false description: 'version of Node.js to use' type: string + needsRust: + required: false + description: 'if rust is needed' + type: string needsNextest: required: false description: 'if nextest rust dep is needed' @@ -27,15 +35,20 @@ on: required: false description: 'if swc artifact needs uploading' type: string + uploadTestTrace: + required: false + description: 'if test trace needs uploading' + type: string env: NAPI_CLI_VERSION: 2.14.7 TURBO_VERSION: 1.9.6 - RUST_TOOLCHAIN: nightly-2023-03-09 PNPM_VERSION: 7.24.3 NODE_MAINTENANCE_VERSION: 16 NODE_LTS_VERSION: 18 TEST_CONCURRENCY: 6 + # disable backtrace for test snapshots + RUST_BACKTRACE: 0 TURBO_TEAM: 'vercel' TURBO_REMOTE_ONLY: 'true' @@ -43,11 +56,15 @@ env: # we build a dev binary for use in CI so skip downloading # canary next-swc binaries in the monorepo NEXT_SKIP_NATIVE_POSTINSTALL: 1 + DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + DATADOG_TRACE_NEXTJS_TEST: 'true' + DD_ENV: 'ci' TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} NEXT_TEST_JOB: 1 jobs: build: + timeout-minutes: 25 runs-on: [self-hosted, linux, x64] steps: @@ -56,11 +73,15 @@ jobs: - run: node -v - run: pwd - - name: Install - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + # local action -> needs to run after checkout + - name: Install Rust + uses: ./.github/actions/setup-rust + if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }} with: - profile: minimal - toolchain: ${{ env.RUST_TOOLCHAIN }} components: rustfmt, clippy - name: Install nextest @@ -68,15 +89,15 @@ jobs: uses: taiki-e/install-action@nextest - run: rustc --version + if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }} - run: npm i -g yarn "pnpm@${PNPM_VERSION}" "turbo@${TURBO_VERSION}" "@napi-rs/cli@${NAPI_CLI_VERSION}" - - uses: actions/checkout@v3 - with: - fetch-depth: 25 - # clean up any previous artifacts to avoid hitting disk space limits - - run: git clean -xdf && rm -rf /tmp/next-repo-*; rm -rf /tmp/next-install-* /tmp/yarn-* /tmp/ncc-cache target && cargo clean + - run: git clean -xdf && rm -rf /tmp/next-repo-*; rm -rf /tmp/next-install-* /tmp/yarn-* /tmp/ncc-cache target + + - run: cargo clean + if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }} - run: echo "DOCS_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT name: check docs only change @@ -87,6 +108,7 @@ jobs: name: normalize versions - run: turbo run build-native-release --summarize -- --target x86_64-unknown-linux-gnu + if: ${{ inputs.skipNativeBuild != 'yes' }} - name: Upload next-swc artifact if: ${{ inputs.uploadSwcArtifact == 'yes' }} @@ -123,3 +145,34 @@ jobs: with: name: turbo run summary path: .turbo/runs + + - name: Upload test reports artifact + uses: actions/upload-artifact@v3 + if: ${{ inputs.afterBuild }} + with: + name: Test trace reports + path: | + test/test-junit-report + test/turbopack-test-junit-report + if-no-files-found: ignore + + - name: Download test reports artifact + id: download-test-reports + uses: actions/download-artifact@v3 + continue-on-error: true + if: ${{ inputs.uploadTestTrace == 'yes' && (inputs.skipForDocsOnly != 'yes' || steps.docs-change.outputs.DOCS_CHANGE == 'nope') }} + with: + name: Test trace reports + path: test + + - name: Upload test trace to datadog + if: ${{ inputs.uploadTestTrace == 'yes' && (inputs.skipForDocsOnly != 'yes' || steps.docs-change.outputs.DOCS_CHANGE == 'nope') }} + continue-on-error: true + run: | + ls -al ./test + npm install -g junit-report-merger@6.0.2 @datadog/datadog-ci@2.14.0 + jrm ./nextjs-test-result-junit.xml "test/test-junit-report/**/*.xml" + jrm ./turbopack-test-result-junit.xml "test/turbopack-test-junit-report/**/*.xml" + # Put a separate tag for the tests with turbopack to distinguish between same test names + DD_ENV=ci datadog-ci junit upload --tags test.type:nextjs --service nextjs ./nextjs-test-result-junit.xml + DD_ENV=ci datadog-ci junit upload --tags test.type:turbopack --service nextjs ./turbopack-test-result-junit.xml diff --git a/.github/workflows/pull_request_stats.yml b/.github/workflows/pull_request_stats.yml index 77f3acb697283..0b061b8291029 100644 --- a/.github/workflows/pull_request_stats.yml +++ b/.github/workflows/pull_request_stats.yml @@ -7,7 +7,6 @@ name: Generate Pull Request Stats env: NAPI_CLI_VERSION: 2.14.7 TURBO_VERSION: 1.9.6 - RUST_TOOLCHAIN: nightly-2023-03-09 PNPM_VERSION: 7.24.3 NODE_MAINTENANCE_VERSION: 16 NODE_LTS_VERSION: 18 @@ -21,6 +20,7 @@ env: NEXT_SKIP_NATIVE_POSTINSTALL: 1 TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} NEXT_TEST_JOB: 1 + NEXT_DISABLE_SWC_WASM: 1 jobs: build: diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index c0962d685af22..1c36715db4ccd 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -26,7 +26,6 @@ name: Trigger Release env: NAPI_CLI_VERSION: 2.14.7 TURBO_VERSION: 1.9.6 - RUST_TOOLCHAIN: nightly-2023-03-09 PNPM_VERSION: 7.24.3 NODE_MAINTENANCE_VERSION: 16 NODE_LTS_VERSION: 18 diff --git a/.prettierignore b/.prettierignore index baf4892dfbfbb..03038daae8a1a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -40,3 +40,4 @@ bench/nested-deps/pages/**/* bench/nested-deps/components/**/* **/convex/_generated/** +**/.tina/__generated__/** diff --git a/.vercel.approvers b/.vercel.approvers new file mode 100644 index 0000000000000..7cac08a87245c --- /dev/null +++ b/.vercel.approvers @@ -0,0 +1,19 @@ +# Global Owners +@timneutkens +@ijjk +@shuding +@huozhi +@feedthejim +@leerob:optional +@vercel/next.js:optional + +# Tooling & Telemetry +pnpm-lock.yaml @vercel/web-tooling +Cargo.toml @vercel/web-tooling +Cargo.lock @vercel/web-tooling +/.cargo/config.toml @vercel/web-tooling +/.config/nextest.toml @vercel/web-tooling + +# Catch any markdown files outside docs (e.g. examples) +**/*.md @vercel/devex +**/*.mdx @vercel/devex diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 75f549ad6d9a4..b37ae9cdb17c8 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -19,6 +19,9 @@ "ms-vsliveshare.vsliveshare", // Debugging - "ms-vscode.vscode-js-profile-flame" + "ms-vscode.vscode-js-profile-flame", + + // MDX Authoring + "unifiedjs.vscode-mdx" ] } diff --git a/Cargo.lock b/Cargo.lock index ba2e3419defb9..ef41ecd4a696e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,12 @@ dependencies = [ "backtrace", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.2" @@ -353,16 +359,16 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.17.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb" +checksum = "8e6acf7e4a267eecbb127ed696bb2d50572c22ba7f586a646321e1798d8336a1" dependencies = [ "futures-io", "futures-util", "log", "pin-project-lite", "tokio", - "tungstenite 0.17.3", + "tungstenite", ] [[package]] @@ -394,7 +400,7 @@ dependencies = [ [[package]] name = "auto-hash-map" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "serde", ] @@ -521,9 +527,9 @@ dependencies = [ [[package]] name = "binding_macros" -version = "0.50.31" +version = "0.50.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee27f9ffe528bb0a1e94d858d70217ce12076ef01978f73ce4ed50a20dedcc" +checksum = "ef3a809ca2ce465f8c44acb578f9730234a0faf9199590cd5e00e537bb2affa2" dependencies = [ "anyhow", "console_error_panic_hook", @@ -573,6 +579,20 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if 1.0.0", + "constant_time_eq", + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -729,15 +749,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chromiumoxide" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5506e432f602b1747e8a0d60ac6607c6977af4ee9720237764170305323e62" +checksum = "1fbef58698a487c253c55c3d17bb1efbe268d2961a2c8278e3f86fff721355fc" dependencies = [ "async-tungstenite", - "base64 0.13.1", + "base64 0.21.0", "cfg-if 1.0.0", "chromiumoxide_cdp", "chromiumoxide_types", + "dunce", "fnv", "futures", "futures-timer", @@ -754,9 +775,9 @@ dependencies = [ [[package]] name = "chromiumoxide_cdp" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b6988af5c6bbf097999e7db879729dd7b27a62010c482d4922fddeb4f220d4" +checksum = "902b90e019dff479bf5a36ed3961e955afa48c35fb2d4245d0b193e7746d50b9" dependencies = [ "chromiumoxide_pdl", "chromiumoxide_types", @@ -766,9 +787,9 @@ dependencies = [ [[package]] name = "chromiumoxide_pdl" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdf6513e24d260548345a5ef13a04110f5915b7764c274933e10f9363a43e3b" +checksum = "cc9319fb29ecce08ac90dd5a798c391f6a8ae1d7c90aff71f3fa27cb3cdfc3ec" dependencies = [ "chromiumoxide_types", "either", @@ -783,9 +804,9 @@ dependencies = [ [[package]] name = "chromiumoxide_types" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1af9c183b5aac7f09639cc7b4ddde8a8551850d2c9bf36530830cb10e28e676f" +checksum = "0c9187058637b8e555690935a6d25a1f7af1d71b377fc45b4257712efb34551f" dependencies = [ "serde", "serde_json", @@ -1014,6 +1035,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constant_time_eq" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" + [[package]] name = "convert_case" version = "0.5.0" @@ -1518,6 +1545,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -1819,6 +1847,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -2006,6 +2040,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.26.2" @@ -2329,7 +2373,7 @@ dependencies = [ "pin-project", "tokio", "tokio-tungstenite", - "tungstenite 0.18.0", + "tungstenite", ] [[package]] @@ -2393,6 +2437,7 @@ dependencies = [ "bytemuck", "byteorder", "color_quant", + "gif", "jpeg-decoder", "num-rational", "num-traits", @@ -3284,7 +3329,7 @@ dependencies = [ "owo-colors", "parking_lot", "portpicker", - "rand", + "rand 0.8.5", "regex", "serde", "serde_json", @@ -3292,7 +3337,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "tungstenite 0.17.3", + "tungstenite", "turbo-tasks", "turbopack-binding", "url", @@ -3316,13 +3361,14 @@ dependencies = [ "next-dev", "owo-colors", "parking_lot", - "rand", + "rand 0.8.5", "regex", "serde", "serde_json", + "tempdir", "testing", "tokio", - "tungstenite 0.17.3", + "tungstenite", "turbo-tasks", "turbopack-binding", "url", @@ -3425,7 +3471,7 @@ dependencies = [ [[package]] name = "node-file-trace" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "serde", @@ -3853,7 +3899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -3991,7 +4037,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9" dependencies = [ - "rand", + "rand 0.8.5", ] [[package]] @@ -4208,6 +4254,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426" +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.9", +] + [[package]] name = "rand" version = "0.8.5" @@ -4216,7 +4275,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -4226,9 +4285,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -4266,6 +4340,15 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.1.57" @@ -4363,6 +4446,15 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "rend" version = "0.4.0" @@ -4736,7 +4828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a4591a2d128af73b1b819ab95f143bc6a2fbe48cd23a4c45e1ee32177e66ae6" dependencies = [ "once_cell", - "rand", + "rand 0.8.5", "sentry-types", "serde", "serde_json", @@ -5310,6 +5402,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "supports-color" version = "1.3.1" @@ -5340,9 +5438,9 @@ dependencies = [ [[package]] name = "swc" -version = "0.261.31" +version = "0.261.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cbbf554d5643f670e66ca9094dd375c7fc060106202b17da1729d36c1fb711" +checksum = "1b59f4e1f5ebb10037de0a3a5c25d2fe7691f8811f42d3549fb81f2b9047205b" dependencies = [ "ahash", "anyhow", @@ -5408,9 +5506,9 @@ dependencies = [ [[package]] name = "swc_bundler" -version = "0.214.23" +version = "0.214.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc8061fcf233b059656a3c20e098971c6302346da23dc6cf21cb468335c27a1c" +checksum = "1a22f0573fef09dffc3db7094d29ef9445b4f09b76da650bc870060ca0b8c8a4" dependencies = [ "ahash", "anyhow", @@ -5514,9 +5612,9 @@ dependencies = [ [[package]] name = "swc_core" -version = "0.76.37" +version = "0.76.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c40087bc20b5e2611bef9a02924ed990f04a446f0afc0d9a748382f797f948" +checksum = "82b4ef70c1c82e3392058c2a497e2fc787ed0df3b92dddbbef2e864d866cfa7f" dependencies = [ "binding_macros", "swc", @@ -5648,9 +5746,9 @@ dependencies = [ [[package]] name = "swc_css_prefixer" -version = "0.149.14" +version = "0.149.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ada0aafa05a52012e4409976dd3352f1986701b7b7756946700543b4b2ebec9c" +checksum = "13d84e4959647c275a6eaf3c40c2f830b2dd3c169149a5d5c03734cfad7f839c" dependencies = [ "once_cell", "preset_env_base", @@ -5712,9 +5810,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.139.15" +version = "0.139.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c6af8e6d6714ecd7ef5cfba322aa1b436f78d9a82b0c3ff16aeaf97b65cd6d" +checksum = "c66d1ea16bb9b7ea6f87f17325742ff256fcbd65b188af57c2bf415fe4afc945" dependencies = [ "memchr", "num-bigint", @@ -5758,9 +5856,9 @@ dependencies = [ [[package]] name = "swc_ecma_lints" -version = "0.82.16" +version = "0.82.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3ea8c34004c75da37b7c4f70d1f043f4de455e6a6e78440bfa4d24a3bd201d" +checksum = "48d5b6aadff572b212ceadba56da45a6bb9af7d67ce1c234a27073d4ec4e6361" dependencies = [ "ahash", "auto_impl", @@ -5801,9 +5899,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.181.23" +version = "0.181.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f054b72aee9b9e92568c8045d8bd2f1622e7caed54a83c70613c36c25e209fd6" +checksum = "0f85eb56b6c5a8ba4e91141602511b9c5a390bad4c8e454bff77d80c2033c265" dependencies = [ "ahash", "arrayvec", @@ -5857,9 +5955,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.195.22" +version = "0.195.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71c3e40213af349ff5c873963cc1aa03add9b2d1a891dca94590aaed889c216" +checksum = "384802032a00bc0e67993e7c3c9a28b82af19c5685eb9d8f93655376f3f823c2" dependencies = [ "ahash", "anyhow", @@ -5912,9 +6010,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "0.218.20" +version = "0.218.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f359eeb44c37ae4fda275f02a8e718c750ad2b0c8ce2254aca624925be8c8943" +checksum = "dbe5ca1c16b4ea9ece9f43a24554edce402641c46b2cc4e418be6c40897572b0" dependencies = [ "swc_atoms", "swc_common", @@ -5932,9 +6030,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.127.16" +version = "0.127.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6232e641bef05c462bc7da34a3771f9b3f1f3352349ae0cd72b8eee8b0f5d5e0" +checksum = "f9c33ec5369178f3a0580ab86cfe89ffb9c3fbd122aed379cfb71d469d9d61c1" dependencies = [ "better_scoped_tls", "bitflags 2.2.1", @@ -5956,9 +6054,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.116.16" +version = "0.116.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f086829a3e645382f5609c9c6dce1d29e5204b3c81f82fe8d65d3bf17bcca68b" +checksum = "6e3b0d5f362f0da97be1f1b06d7b0d8667ea70b4adeabff0dcaecb6259c09525" dependencies = [ "swc_atoms", "swc_common", @@ -5970,9 +6068,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.153.18" +version = "0.153.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1ce5cb1e660b972799a3f28587e01d665981661753cb8f3eb7db89e77dae28" +checksum = "851739faeec27389fbfe5b170534df7868bbcc5c78f229f22fce43f004858eca" dependencies = [ "ahash", "arrayvec", @@ -6010,9 +6108,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "0.170.18" +version = "0.170.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0dfc1f9d2dfa4a95cb10dea599ddf61506e35f5eafbac25cd14bc33f413a7c" +checksum = "b1595b0522333346488ad7354ad55d4ed337d4e5d7ded5e4d468c523ecb9a495" dependencies = [ "Inflector", "ahash", @@ -6038,9 +6136,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.187.20" +version = "0.187.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa08d0945505263edb8f1fadc71a8afdf530f582d1a133c434055cc4d76298c" +checksum = "3fff2641ba155b3aaa8ef15c1888d75b73e9f10431ec1cb58e66598266199322" dependencies = [ "ahash", "dashmap", @@ -6064,9 +6162,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.161.20" +version = "0.161.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416fbb84f84644ef0e81df80bf44fd575bbb297a78887e359e16a61f6dc5af86" +checksum = "593d2c321aa9b29b289d144832d985031ddc453ab1fdf59170d810e2283944f2" dependencies = [ "either", "rustc-hash", @@ -6084,9 +6182,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.173.18" +version = "0.173.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39a0de45fa34ee797a1c80497c8b9dcb6cf6e56b455c163453399894c58a812" +checksum = "5fb9481ad4e2acba34c6fbb6d4ccc64efe9f1821675e883dcfa732d7220f4b1e" dependencies = [ "ahash", "base64 0.13.1", @@ -6110,9 +6208,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_testing" -version = "0.130.16" +version = "0.130.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2f974e4f1d78309ea24cce2631a664eb367347c9d2e6677f96c7b8b6b176d" +checksum = "01e7727573d1ea0a9d3f24c11fe4cb5038ce904bf3e67a13c0ee4a07d3ae4a1d" dependencies = [ "ansi_term", "anyhow", @@ -6136,9 +6234,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.177.20" +version = "0.177.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340cc027a6e87966715005b94e3a7ac95baf76c80b8aedad8afdd1c134740c80" +checksum = "64f65165a1ef99b7638503fcf6ead4f58b7cfbf3a37c75dfd6f541b491323041" dependencies = [ "serde", "swc_atoms", @@ -6152,9 +6250,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "0.13.15" +version = "0.13.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aacc5022f52ae332c6545b9248b70285be1847cf85d48b0640d05f68ff971f" +checksum = "b7da59ebce19380671e76cfe7e9b0cdd6f430b3090c65c24aefcc07ed63739f2" dependencies = [ "ahash", "indexmap", @@ -6323,9 +6421,9 @@ dependencies = [ [[package]] name = "swc_plugin_runner" -version = "0.95.16" +version = "0.95.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d8dc9624bfd27ec09d4dcf56d9e0ed5faaef056629f5d190fc69fde46ec489" +checksum = "32cea3fce3d59a0962dc427019c469cd44a68c52224af1c7cc2a93f0d41d5890" dependencies = [ "anyhow", "enumset", @@ -6338,6 +6436,7 @@ dependencies = [ "swc_plugin_proxy", "tracing", "wasmer", + "wasmer-cache", "wasmer-compiler-cranelift", "wasmer-wasix", ] @@ -6451,6 +6550,16 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + [[package]] name = "tempfile" version = "3.5.0" @@ -6774,7 +6883,7 @@ dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.18.0", + "tungstenite", ] [[package]] @@ -6860,7 +6969,7 @@ dependencies = [ "indexmap", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -6994,25 +7103,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "tungstenite" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "sha-1", - "thiserror", - "url", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.18.0" @@ -7025,7 +7115,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.8.5", "sha1 0.10.5", "thiserror", "url", @@ -7035,7 +7125,7 @@ dependencies = [ [[package]] name = "turbo-tasks" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "auto-hash-map", @@ -7066,7 +7156,7 @@ dependencies = [ [[package]] name = "turbo-tasks-build" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "cargo-lock", @@ -7078,7 +7168,7 @@ dependencies = [ [[package]] name = "turbo-tasks-bytes" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "bytes", @@ -7093,7 +7183,7 @@ dependencies = [ [[package]] name = "turbo-tasks-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "dotenvy", @@ -7107,7 +7197,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fetch" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "indexmap", @@ -7124,7 +7214,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fs" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "auto-hash-map", @@ -7154,7 +7244,7 @@ dependencies = [ [[package]] name = "turbo-tasks-hash" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "base16", "hex", @@ -7166,7 +7256,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -7180,7 +7270,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros-shared" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "proc-macro2", "quote", @@ -7190,7 +7280,7 @@ dependencies = [ [[package]] name = "turbo-tasks-malloc" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "mimalloc", ] @@ -7198,7 +7288,7 @@ dependencies = [ [[package]] name = "turbo-tasks-memory" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "auto-hash-map", @@ -7221,7 +7311,7 @@ dependencies = [ [[package]] name = "turbo-tasks-testing" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "auto-hash-map", @@ -7233,7 +7323,7 @@ dependencies = [ [[package]] name = "turbopack" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "async-recursion", @@ -7263,7 +7353,7 @@ dependencies = [ [[package]] name = "turbopack-bench" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "chromiumoxide", @@ -7276,13 +7366,13 @@ dependencies = [ "owo-colors", "parking_lot", "portpicker", - "rand", + "rand 0.8.5", "regex", "serde", "serde_json", "tempfile", "tokio", - "tungstenite 0.17.3", + "tungstenite", "turbo-tasks", "turbo-tasks-testing", "turbopack-create-test-app", @@ -7293,7 +7383,7 @@ dependencies = [ [[package]] name = "turbopack-binding" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "auto-hash-map", "mdxjs", @@ -7334,7 +7424,7 @@ dependencies = [ [[package]] name = "turbopack-cli-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "clap 4.1.11", @@ -7358,7 +7448,7 @@ dependencies = [ [[package]] name = "turbopack-core" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "async-trait", @@ -7386,7 +7476,7 @@ dependencies = [ [[package]] name = "turbopack-create-test-app" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "clap 4.1.11", @@ -7399,7 +7489,7 @@ dependencies = [ [[package]] name = "turbopack-css" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "async-trait", @@ -7421,7 +7511,7 @@ dependencies = [ [[package]] name = "turbopack-dev" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "indexmap", @@ -7445,7 +7535,7 @@ dependencies = [ [[package]] name = "turbopack-dev-server" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "async-compression", @@ -7480,7 +7570,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "async-trait", @@ -7513,7 +7603,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-plugins" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "async-trait", @@ -7536,7 +7626,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-runtime" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "indoc", @@ -7553,7 +7643,7 @@ dependencies = [ [[package]] name = "turbopack-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "indexmap", @@ -7569,7 +7659,7 @@ dependencies = [ [[package]] name = "turbopack-image" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "base64 0.21.0", @@ -7589,7 +7679,7 @@ dependencies = [ [[package]] name = "turbopack-json" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "serde", @@ -7604,7 +7694,7 @@ dependencies = [ [[package]] name = "turbopack-mdx" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "mdxjs", @@ -7619,7 +7709,7 @@ dependencies = [ [[package]] name = "turbopack-node" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "async-stream", @@ -7654,7 +7744,7 @@ dependencies = [ [[package]] name = "turbopack-static" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "serde", @@ -7670,7 +7760,7 @@ dependencies = [ [[package]] name = "turbopack-swc-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "swc_core", "turbo-tasks", @@ -7681,7 +7771,7 @@ dependencies = [ [[package]] name = "turbopack-test-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230531.2#9f86f2ed10c4d20e7d1b696cce7dc9ed928be0c2" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230612.1#6c55f3ddc263cd30e6f776611147d487323271b0" dependencies = [ "anyhow", "once_cell", @@ -7700,7 +7790,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if 1.0.0", - "rand", + "rand 0.4.6", "static_assertions", ] @@ -8226,6 +8316,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "wasmer-cache" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f0de969b05cc3c11196beeb46e5868a3712a187d777ee94113f7258c2ec121c" +dependencies = [ + "blake3", + "hex", + "thiserror", + "wasmer", +] + [[package]] name = "wasmer-compiler" version = "3.3.0" @@ -8347,7 +8449,7 @@ dependencies = [ "linked_hash_set", "once_cell", "pin-project", - "rand", + "rand 0.8.5", "serde", "serde_derive", "serde_json", @@ -8475,7 +8577,7 @@ dependencies = [ "memmap2", "once_cell", "path-clean", - "rand", + "rand 0.8.5", "serde", "serde_cbor", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 72aede3e589ca..675f1f9204d4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,15 +38,15 @@ next-transform-strip-page-exports = { path = "packages/next-swc/crates/next-tran # SWC crates # Keep consistent with preset_env_base through swc_core -swc_core = { version = "0.76.37" } +swc_core = { version = "0.76.46" } testing = { version = "0.33.13" } # Turbo crates -turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230601.3" } +turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230612.1" } # [TODO]: need to refactor embed_directory! macro usages, as well as resolving turbo_tasks::function, macros.. -turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230601.3" } +turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230612.1" } # [TODO]: need to refactor embed_directory! macro usage in next-core -turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230601.3" } +turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230612.1" } # General Deps @@ -56,12 +56,12 @@ turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack # and some aren't buildable with rustls. reqwest = { version = "0.11.14", default-features = false } -chromiumoxide = { version = "0.4.0", features = [ +chromiumoxide = { version = "0.5.0", features = [ "tokio-runtime", ], default-features = false } # For matching on errors from chromiumoxide. Keep in # sync with chromiumoxide's tungstenite requirement. -tungstenite = "0.17.3" +tungstenite = "0.18.0" # flate2_zlib requires zlib, use flate2_rust allsorts = { version = "0.14.0", default_features = false, features = [ diff --git a/contributing/.vercel.approvers b/contributing/.vercel.approvers new file mode 100644 index 0000000000000..07ccd3888a9a1 --- /dev/null +++ b/contributing/.vercel.approvers @@ -0,0 +1 @@ +@vercel/devex diff --git a/contributing/core/developing-using-local-app.md b/contributing/core/developing-using-local-app.md index bb7a5e51c35f2..f61d462348708 100644 --- a/contributing/core/developing-using-local-app.md +++ b/contributing/core/developing-using-local-app.md @@ -41,10 +41,12 @@ Failed to load SWC binary, see more info here: https://nextjs.org/docs/messages/ Try to add the below section to your `package.json`, then run again ```json -"optionalDependencies": { - "@next/swc-linux-x64-gnu": "canary", - "@next/swc-win32-x64-msvc": "canary", - "@next/swc-darwin-x64": "canary", - "@next/swc-darwin-arm64": "canary" -}, +{ + "optionalDependencies": { + "@next/swc-linux-x64-gnu": "canary", + "@next/swc-win32-x64-msvc": "canary", + "@next/swc-darwin-x64": "canary", + "@next/swc-darwin-arm64": "canary" + } +} ``` diff --git a/contributing/docs/adding-documentation.md b/contributing/docs/adding-documentation.md index 3df38841d024e..5067e7d3f5373 100644 --- a/contributing/docs/adding-documentation.md +++ b/contributing/docs/adding-documentation.md @@ -1,40 +1,3 @@ -# Updating Documentation Paths +# Contributing to Documentation -Our documentation currently leverages a [manifest file](/docs/manifest.json), which is how documentation entries are checked. - -When adding a new entry under an existing category you only need to add an entry with `{title: '', path: '/docs/path/to/file.md'}`. The "title" is what is shown on the sidebar. - -When moving the location/url of an entry, the "title" field can be removed from the existing entry and the ".md" extension removed from the "path", then a "redirect" field with the shape of `{permanent: true/false, destination: '/some-url'}` can be added. A new entry should be added with the "title" and "path" fields if the document is renamed within the [`docs` folder](/docs) that points to the new location in the folder, e.g. `/docs/some-url.md` - -Example of moving documentation file: - -Before: - -```json -[ - { - "path": "/docs/original.md", - "title": "Hello world" - } -] -``` - -After: - -```json -[ - { - "path": "/docs/original", - "redirect": { - "permanent": false, - "destination": "/new" - } - }, - { - "path": "/docs/new.md", - "title": "Hello world" - } -] -``` - -> **Note**: The manifest is checked automatically in the "lint" step in CI when opening a PR. +See the [Docs Contribution Guide](https://nextjs.org/docs/community/contribution-guide) to learn how to contribute to the Next.js Documentation. diff --git a/docs/.vercel.approvers b/docs/.vercel.approvers new file mode 100644 index 0000000000000..752c3015b5a0a --- /dev/null +++ b/docs/.vercel.approvers @@ -0,0 +1 @@ +@vercel/devex \ No newline at end of file diff --git a/docs/01-getting-started/01-installation.mdx b/docs/01-getting-started/01-installation.mdx new file mode 100644 index 0000000000000..afe5228201224 --- /dev/null +++ b/docs/01-getting-started/01-installation.mdx @@ -0,0 +1,134 @@ +--- +title: Installation +description: Create a new Next.js application with `create-next-app`. Set up TypeScript, styles, and configure your `next.config.js` file. +related: + title: Next Steps + description: For more information on what to do next, we recommend the following sections + links: + - getting-started/react-essentials + - app/building-your-application + - app/building-your-application/configuring/typescript +--- + +System Requirements: + +- [Node.js 16.8](https://nodejs.org/) or later. +- macOS, Windows (including WSL), and Linux are supported. + +## Automatic Installation + +We recommend creating a new Next.js app using `create-next-app`, which sets up everything automatically for you. To create a project, run: + +```bash filename="Terminal" +npx create-next-app@latest +``` + +On installation, you'll see the following prompts: + +```txt filename="Terminal" +What is your project named? my-app +Would you like to use TypeScript with this project? No / Yes +Would you like to use ESLint with this project? No / Yes +Would you like to use Tailwind CSS with this project? No / Yes +Would you like to use `src/` directory with this project? No / Yes +Use App Router (recommended)? No / Yes +Would you like to customize the default import alias? No / Yes +``` + +Next.js now ships with TypeScript, ESLint, and Tailwind CSS configuration by default. You can also choose to use the `src` directory for your application code. + +After the prompts, `create-next-app` will create a folder with your project name and install the required dependencies. + +> **Good to know**: While you can use the [Pages Router](/docs/pages) in your new project. We recommend starting new applications with the App Router to leverage React's latest features. + +## Manual Installation + +To manually create a new Next.js app, install the required packages: + +```bash filename="Terminal" +npm install next@latest react@latest react-dom@latest +``` + +Open `package.json` and add the following `scripts`: + +```json filename="package.json" +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + } +} +``` + +These scripts refer to the different stages of developing an application: + +- `dev`: runs [`next dev`](/docs/app/api-reference/next-cli#development) to start Next.js in development mode. +- `build`: runs [`next build`](/docs/app/api-reference/next-cli#build) to build the application for production usage. +- `start`: runs [`next start`](/docs/app/api-reference/next-cli#production) to start a Next.js production server. +- `lint`: runs [`next lint`](/docs/app/api-reference/next-cli#lint) to set up Next.js' built-in ESLint configuration. + +### Create the `app` folder + +Next, create an `app` folder and add a `layout.tsx` and `page.tsx` file. These will be rendered when the user visits the root of your application. + +App Folder Structure + +Create a [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) inside `app/layout.tsx` with the required `` and `` tags: + +```tsx filename="app/layout.tsx" switcher +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +Finally, create a home page `app/page.tsx` with some initial content: + +```tsx filename="app/page.tsx" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` + +```jsx filename="app/page.js" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` + +> **Good to know**: If you forget to create `layout.tsx`, Next.js will automatically create this file for you when running the development server with `next dev`. + +### Create the `public` folder + +You can optionally create a `public` folder to store static assets such as images, fonts, etc. Files inside `public` directory can then be referenced by your code starting from the base URL (`/`). + +## Run the Development Server + +1. Run `npm run dev` to start the development server. +2. Visit `http://localhost:3000` to view your application. +3. Edit `app/layout.tsx` or `app/page.tsx` and save to see the updated result in your browser. diff --git a/docs/01-getting-started/02-project-structure.mdx b/docs/01-getting-started/02-project-structure.mdx new file mode 100644 index 0000000000000..00fc12d0472a7 --- /dev/null +++ b/docs/01-getting-started/02-project-structure.mdx @@ -0,0 +1,150 @@ +--- +title: Next.js Project Structure +nav_title: Project Structure +description: A list of folders and files conventions in a Next.js project +--- + +This page provides an overview of the file and folder structure of a Next.js project. It covers top-level files and folders, configuration files, and routing conventions within the `app` and `pages` directories. + +## Top-level files + +| | | +| ------------------------------------------------------------------------------------------- | --------------------------------------- | +| **Next.js** | | +| [`next.config.js`](/docs/app/api-reference/next-config-js) | Configuration file for Next.js | +| [`middleware.ts`](/docs/app/building-your-application/routing/middleware) | Next.js request middleware | +| [`instrumentation.ts`](/docs/app/building-your-application/optimizing/instrumentation) | OpenTelemetry and Instrumentation | +| [`.env`](/docs/app/building-your-application/configuring/environment-variables) | Environment variables | +| [`.env.local`](/docs/app/building-your-application/configuring/environment-variables) | Local environment variables | +| [`.env.production`](/docs/app/building-your-application/configuring/environment-variables) | Production environment variables | +| [`.env.development`](/docs/app/building-your-application/configuring/environment-variables) | Development environment variables | +| `.next-env.d.ts` | TypeScript declaration file for Next.js | +| **Ecosystem** | | +| [`package.json`](/docs/getting-started/installation#manual-installation) | Project dependencies and scripts | +| `.gitignore` | Git files and folders to ignore | +| `tsconfig.json` | Configuration file for TypeScript | +| `jsconfig.json` | Configuration file for JavaScript | +| [`.eslintrc.json`](/docs/app/building-your-application/configuring/eslint) | Configuration file for ESLint | + +## Top-level folders + +| | | +| ----------------------------------------------------------------------- | ---------------------------------- | +| [`app`](/docs/app/building-your-application/routing) | App Router | +| [`pages`](/docs/pages/building-your-application/routing) | Pages Router | +| [`public`](/docs/getting-started/installation#create-the-public-folder) | Static assets to be served | +| [`src`](/docs/app/building-your-application/configuring/src-directory) | Optional application source folder | + +## `app` Routing Conventions + +### Routing Files + +| | | | +| ------------------------------------------------------------------------------- | ------------------- | ---------------------------- | +| [`layout`](/docs/app/api-reference/file-conventions/layout) | `.js` `.jsx` `.tsx` | Layout | +| [`page`](/docs/app/api-reference/file-conventions/page) | `.js` `.jsx` `.tsx` | Page | +| [`loading`](/docs/app/api-reference/file-conventions/loading) | `.js` `.jsx` `.tsx` | Loading UI | +| [`not-found`](/docs/app/api-reference/file-conventions/not-found) | `.js` `.jsx` `.tsx` | Not found UI | +| [`error`](/docs/app/api-reference/file-conventions/error) | `.js` `.jsx` `.tsx` | Error UI | +| [`global-error`](/docs/app/api-reference/file-conventions/error#global-errorjs) | `.js` `.jsx` `.tsx` | Global error UI | +| [`route`](/docs/app/api-reference/file-conventions/route) | `.js` `.ts` | API endpoint | +| [`template`](/docs/app/api-reference/file-conventions/template) | `.js` `.jsx` `.tsx` | Re-rendered layout | +| [`default`](/docs/app/api-reference/file-conventions/default) | `.js` `.jsx` `.tsx` | Parallel route fallback page | + +### Nested Routes + +| | | +| ---------------------------------------------------------------------------- | -------------------- | +| [`folder`](/docs/app/building-your-application/routing#route-segments) | Route segment | +| [`folder/folder`](/docs/app/building-your-application/routing#nested-routes) | Nested route segment | + +### Dynamic Routes + +| | | +| --------------------------------------------------------------------------------------------------------- | --------------------------- | +| [`[folder]`](/docs/app/building-your-application/routing/dynamic-routes#convention) | Dynamic route segment | +| [`[...folder]`](/docs/app/building-your-application/routing/dynamic-routes) | Catch-all segments | +| [`[[...folder]]`](/docs/app/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | Optional catch-all segments | + +### Route Groups and Private Folders + +| | | +| ----------------------------------------------------------------------------------- | ------------------------------------------------ | +| [`(folder)`](/docs/app/building-your-application/routing/route-groups#convention) | Group routes without affecting routing | +| [`_folder`](/docs/app/building-your-application/routing/colocation#private-folders) | Opt folder and all child segments out of routing | + +### Parallel and Intercepted Routes + +| | | +| ---------------------------------------------------------------------------------------------- | -------------------------- | +| [`@folder`](/docs/app/building-your-application/routing/parallel-routes#convention) | Named slot | +| [`(.)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept same level | +| [`(..)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept one level above | +| [`(..)(..)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept two levels above | +| [`(...)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept from root | + +### Metadata File Conventions + +#### App Icons + +| | | | +| --------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------ | +| [`favicon`](/docs/app/api-reference/file-conventions/metadata/app-icons#favicon) | `.ico` | Favicon file | +| [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#icon) | `.png` `.svg` | App Icon file | +| [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx) | `.ico` `.jpg` `.jpeg` `.png` `.svg` | Generated App Icon | +| [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#apple-icon) | `.jpg` `.jpeg`, `.png` | Apple App Icon file | +| [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated Apple App Icon | + +#### Open Graph and Twitter Images + +| | | | +| --------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------------------------- | +| [`opengraph-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#opengraph-image) | `.jpg` `.jpeg` `.png` `.gif` | Open Graph image file | +| [`opengraph-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#generate-images-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated Open Graph image | +| [`twitter-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#twitter-image) | `.jpg` `.jpeg` `.png` `.gif` | Twitter image file | +| [`twitter-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#generate-images-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated Twitter image | + +#### SEO + +| | | | +| ------------------------------------------------------------------------------------------- | ----------- | --------------------- | +| [`sitemap`](/docs/app/api-reference/file-conventions/metadata/sitemap#static-sitemapxml) | `.xml` | Sitemap file | +| [`sitemap`](/docs/app/api-reference/file-conventions/metadata/sitemap#generate-a-sitemap) | `.js` `.ts` | Generated Sitemap | +| [`robots`](/docs/app/api-reference/file-conventions/metadata/robots#static-robotstxt) | `.txt` | Robots file | +| [`robots`](/docs/app/api-reference/file-conventions/metadata/robots#generate-a-robots-file) | `.js` `.ts` | Generated Robots file | + +## `pages` Routing Conventions + +### Special Files + +| | | | +| ----------------------------------------------------------------------------------------------------------- | ------------------- | ----------------- | +| [`_app`](/docs/pages/building-your-application/routing/custom-app) | `.js` `.jsx` `.tsx` | Custom App | +| [`_document`](/docs/pages/building-your-application/routing/custom-document) | `.js` `.jsx` `.tsx` | Custom Document | +| [`_error`](/docs/pages/building-your-application/routing/custom-error#more-advanced-error-page-customizing) | `.js` `.jsx` `.tsx` | Custom Error Page | +| [`404`](/docs/pages/building-your-application/routing/custom-error#404-page) | `.js` `.jsx` `.tsx` | 404 Error Page | +| [`500`](/docs/pages/building-your-application/routing/custom-error#500-page) | `.js` `.jsx` `.tsx` | 500 Error Page | + +### Routes + +| | | | +| ---------------------------------------------------------------------------------------------- | ------------------- | ----------- | +| **Folder convention** | | | +| [`index`](/docs/pages/building-your-application/routing/pages-and-layouts#index-routes) | `.js` `.jsx` `.tsx` | Home page | +| [`folder/index`](/docs/pages/building-your-application/routing/pages-and-layouts#index-routes) | `.js` `.jsx` `.tsx` | Nested page | +| **File convention** | | | +| [`index`](/docs/pages/building-your-application/routing/pages-and-layouts#index-routes) | `.js` `.jsx` `.tsx` | Home page | +| [`file`](/docs/pages/building-your-application/routing/pages-and-layouts) | `.js` `.jsx` `.tsx` | Nested page | + +### Dynamic Routes + +| | | | +| ----------------------------------------------------------------------------------------------------------------- | ------------------- | --------------------------- | +| **Folder convention** | | | +| [`[folder]/index`](/docs/pages/building-your-application/routing/dynamic-routes) | `.js` `.jsx` `.tsx` | Dynamic route segment | +| [`[...folder]/index`](/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments) | `.js` `.jsx` `.tsx` | Catch-all segments | +| [`[[...folder]]/index`](/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | `.js` `.jsx` `.tsx` | Optional catch-all segments | +| **File convention** | | | +| [`[file]`](/docs/pages/building-your-application/routing/dynamic-routes) | `.js` `.jsx` `.tsx` | Dynamic route segment | +| [`[...file]`](/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments) | `.js` `.jsx` `.tsx` | Catch-all segments | +| [`[[...file]]`](/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | `.js` `.jsx` `.tsx` | Optional catch-all segments | diff --git a/docs/01-getting-started/03-react-essentials.mdx b/docs/01-getting-started/03-react-essentials.mdx new file mode 100644 index 0000000000000..559c478cfd83f --- /dev/null +++ b/docs/01-getting-started/03-react-essentials.mdx @@ -0,0 +1,863 @@ +--- +title: React Essentials +description: An overview of essential React features for building Next.js Applications, including Server Components. +--- + +To build applications with Next.js, it helps to be familiar with React's newer features such as Server Components. This page will go through the differences between Server and Client Components, when to use them, and recommended patterns. + +If you're new to React, we also recommend referring to the [React Docs](https://react.dev/learn). Here are some great resources for learning: + +- [React Tutorial](https://react.dev/learn/tutorial-tic-tac-toe) +- [Thinking in React](https://react.dev/learn/thinking-in-react) +- [Learn React](https://react.dev/learn/describing-the-ui) + +## Server Components + +Server and Client Components allow developers to build applications that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering. + +### Thinking in Server Components + +Similar to how React changed the way we think about building UIs, React Server Components introduce a new mental model for building hybrid applications that leverage the [server and the client](/docs/app/building-your-application/rendering#rendering-environments). + +Instead of React rendering your whole application client-side (such as in the case of Single-Page Applications), React now gives you the flexibility to choose where to render your components based on their purpose. + +For example, consider a [page](/docs/app/building-your-application/routing/pages-and-layouts#pages) in your application: + +Thinking in Server Components + +If we were to split the page into smaller components, you'll notice that the majority of components are non-interactive and can be rendered on the server as Server Components. For smaller pieces of interactive UI, we can _sprinkle in_ Client Components. This aligns with Next.js server-first approach. + +### Why Server Components? + +So, you may be thinking, why Server Components? What are the advantages of using them over Client Components? + +Server Components allow developers to better leverage server infrastructure. For example, you can move data fetching to the server, closer to your database, and keep large dependencies that previously would impact the client JavaScript bundle size on the server, leading to improved performance. Server Components make writing a React application feel similar to PHP or Ruby on Rails, but with the power and flexibility of React and the components model for templating UI. + +With Server Components, the initial page load is faster, and the client-side JavaScript bundle size is reduced. The base client-side runtime is **cacheable** and **predictable** in size, and does _not_ increase as your application grows. Additional JavaScript is _only added_ as client-side interactivity is used in your application through [Client Components](#client-components). + +When a route is loaded with Next.js, the initial HTML is rendered on the server. This HTML is then **progressively enhanced** in the browser, allowing the client to take over the application and add interactivity, by asynchronously loading the Next.js and React client-side runtime. + +To make the transition to Server Components easier, all components inside the [App Router](/docs/app/building-your-application/routing#the-app-router) are Server Components by default, including [special files](/docs/app/building-your-application/routing#file-conventions) and [colocated components](/docs/app/building-your-application/routing#colocation). This allows you to automatically adopt them with no extra work, and achieve great performance out of the box. You can also optionally opt-in to Client Components using the ['use client' directive](#the-use-client-directive). + +## Client Components + +Client Components enable you to add client-side interactivity to your application. In Next.js, they are [pre-rendered](/docs/app/building-your-application/rendering#component-level-client-and-server-rendering) on the server and [hydrated](/docs/app/building-your-application/rendering#component-level-client-and-server-rendering) on the client. You can think of Client Components as how components in the [Pages Router](/docs/pages) have always worked. + +### The "use client" directive + +The [`"use client"` directive](https://github.com/reactjs/rfcs/pull/227) is a convention to declare a boundary between a Server and Client Component module graph. + +```tsx filename="app/counter.tsx" highlight={1} switcher +'use client' + +import { useState } from 'react' + +export default function Counter() { + const [count, setCount] = useState(0) + + return ( +
+

You clicked {count} times

+ +
+ ) +} +``` + +```jsx filename="app/counter.js" highlight={1} switcher +'use client' + +import { useState } from 'react' + +export default function Counter() { + const [count, setCount] = useState(0) + + return ( +
+

You clicked {count} times

+ +
+ ) +} +``` + +Use Client Directive and Network Boundary + +`"use client"` _sits_ between server-only and client code. It's placed at the top of a file, above imports, to define the _cut-off_ point where it crosses the boundary from the server-only to the client part. Once `"use client"` is defined in a file, all other modules imported into it, including child components, are considered part of the client bundle. + +Since Server Components are the default, all components are part of the Server Component module graph unless defined or imported in a module that starts with the `"use client"` directive. + +> **Good to know**: +> +> - Components in the Server Component module graph are guaranteed to be only rendered on the server. +> - Components in the Client Component module graph are primarily rendered on the client, but with Next.js, they can also be pre-rendered on the server and hydrated on the client. +> - The `"use client"` directive must be defined at the **top of a file** before any imports. +> - `"use client"` does **not** need to be defined in every file. The Client module boundary only needs to be defined once, at the "entry point", for all modules imported into it to be considered a Client Component. + +## When to use Server and Client Components? + +To simplify the decision between Server and Client Components, we recommend using Server Components (default in the `app` directory) until you have a use case for a Client Component. + +This table summarizes the different use cases for Server and Client Components: + +| What do you need to do? | Server Component | Client Component | +| ---------------------------------------------------------------------------------- | ------------------- | ------------------- | +| Fetch data. | | | +| Access backend resources (directly) | | | +| Keep sensitive information on the server (access tokens, API keys, etc) | | | +| Keep large dependencies on the server / Reduce client-side JavaScript | | | +| Add interactivity and event listeners (`onClick()`, `onChange()`, etc) | | | +| Use State and Lifecycle Effects (`useState()`, `useReducer()`, `useEffect()`, etc) | | | +| Use browser-only APIs | | | +| Use custom hooks that depend on state, effects, or browser-only APIs | | | +| Use [React Class components](https://react.dev/reference/react/Component) | | | + +## Patterns + +### Moving Client Components to the Leaves + +To improve the performance of your application, we recommend moving Client Components to the leaves of your component tree where possible. + +For example, you may have a Layout that has static elements (e.g. logo, links, etc) and an interactive search bar that uses state. + +Instead of making the whole layout a Client Component, move the interactive logic to a Client Component (e.g. ``) and keep your layout as a Server Component. This means you don't have to send all the component Javascript of the layout to the client. + +```tsx filename="app/layout.tsx" switcher +// SearchBar is a Client Component +import SearchBar from './searchbar' +// Logo is a Server Component +import Logo from './logo' + +// Layout is a Server Component by default +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> + +
{children}
+ + ) +} +``` + +```jsx filename="app/layout.js" switcher +// SearchBar is a Client Component +import SearchBar from './searchbar' +// Logo is a Server Component +import Logo from './logo' + +// Layout is a Server Component by default +export default function Layout({ children }) { + return ( + <> + +
{children}
+ + ) +} +``` + +### Composing Client and Server Components + +Server and Client Components can be combined in the same component tree. + +Behind the scenes, React handles rendering as follows: + +- On the server, React renders **all** Server Components **before** sending the result to the client. + - This includes Server Components nested inside Client Components. + - Client Components encountered during this stage are skipped. +- On the client, React renders Client Components and _slots in_ the rendered result of Server Components, merging the work done on the server and client. + - If any Server Components are nested inside a Client Component, their rendered content will be placed correctly within the Client Component. + +> **Good to know**: In Next.js, during the initial page load, both the rendered result of Server Components from the above step and Client Components are [pre-rendered on the server as HTML](/docs/app/building-your-application/rendering#static-and-dynamic-rendering-on-the-server) to produce a faster initial page load. + +### Nesting Server Components inside Client Components + +Given the rendering flow outlined above, there is a restriction around importing a Server Component into a Client Component, as this approach would require an additional server round trip. + +#### Unsupported Pattern: Importing Server Components into Client Components + +The following pattern is not supported. You cannot import a Server Component into a Client Component: + +```tsx filename="app/example-client-component.tsx" switcher highlight={5,18} +'use client' + +// This pattern will **not** work! +// You cannot import a Server Component into a Client Component. +import ExampleServerComponent from './example-server-component' + +export default function ExampleClientComponent({ + children, +}: { + children: React.ReactNode +}) { + const [count, setCount] = useState(0) + + return ( + <> + + + + + ) +} +``` + +```jsx filename="app/example-client-component.js" switcher highlight={5,14} +'use client' + +// This pattern will **not** work! +// You cannot import a Server Component into a Client Component. +import ExampleServerComponent from './example-server-component' + +export default function ExampleClientComponent({ children }) { + const [count, setCount] = useState(0) + + return ( + <> + + + + + ) +} +``` + +#### Recommended Pattern: Passing Server Components to Client Components as Props + +Instead, when designing Client Components you can use React props to mark _"holes"_ for Server Components. + +The Server Component will be rendered on the server, and when the Client Component is rendered on the client, the _"hole"_ will be filled in with the rendered result of the Server Component. + +A common pattern is to use the React `children` prop to create the _"hole"_. We can refactor `` to accept a generic `children` prop and move the import and explicit nesting of `` up to a parent component. + +```tsx filename="app/example-client-component.tsx" switcher highlight={6,16} +'use client' + +import { useState } from 'react' + +export default function ExampleClientComponent({ + children, +}: { + children: React.ReactNode +}) { + const [count, setCount] = useState(0) + + return ( + <> + + + {children} + + ) +} +``` + +```jsx filename="app/example-client-component.js" switcher highlight={5,12} +'use client' + +import { useState } from 'react' + +export default function ExampleClientComponent({ children }) { + const [count, setCount] = useState(0) + + return ( + <> + + + {children} + + ) +} +``` + +Now, `` has no knowledge of what `children` is. Infact, from its perspective it doesn't even know that `children` will eventually be filled in by the result of a Server Component. + +The only responsibility `ExampleClientComponent` has is to decide where whatever `children` will eventually be should be placed. + +In a parent Server Component, you can import both the `` and `` and pass `` as a child of ``: + +```tsx filename="app/page.tsx" highlight={11} switcher +// This pattern works: +// You can pass a Server Component as a child or prop of a +// Client Component. +import ExampleClientComponent from './example-client-component' +import ExampleServerComponent from './example-server-component' + +// Pages in Next.js are Server Components by default +export default function Page() { + return ( + + + + ) +} +``` + +```jsx filename="app/page.js" highlight={11} switcher +// This pattern works: +// You can pass a Server Component as a child or prop of a +// Client Component. +import ExampleClientComponent from './example-client-component' +import ExampleServerComponent from './example-server-component' + +// Pages in Next.js are Server Components by default +export default function Page() { + return ( + + + + ) +} +``` + +With this approach, the rendering of `` and `` are decoupled and can be rendered independently - aligning with Server Components, which are rendered on the server before Client Components. + +> **Good to know** +> +> - This pattern is **already applied** in [layouts and pages](/docs/app/building-your-application/routing/pages-and-layouts) with the `children` prop so you don't have to create an additional wrapper component. +> - Passing React components (JSX) to other components is not a new concept and has always been part of the React composition model. +> - This composition strategy works across Server and Client Components because the component that receives the prop has no knowledge of **what** the prop is. It is only responsible for where the thing that it is passed should be placed. +> - This allows the passed prop to be rendered independently, in this case, on the server, well before the Client Component is rendered on the client. +> - The very same strategy of "lifting content up" has been used to avoid state changes in a parent component re-rendering an imported nested child component. +> - You're not limited to the `children` prop. You can use any prop to pass JSX. + +### Passing props from Server to Client Components (Serialization) + +Props passed from the Server to Client Components need to be [serializable](https://developer.mozilla.org/en-US/docs/Glossary/Serialization). This means that values such as functions, Dates, etc, cannot be passed directly to Client Components. + +> **Where is the Network Boundary?** +> +> In the App Router, the network boundary is between Server Components and Client Components. This is different from the Pages where the boundary is between `getStaticProps`/`getServerSideProps` and Page Components. Data fetched inside Server Components do not need to be serialized as it doesn't cross the network boundary unless it is passed to a Client Component. Learn more about [data fetching](/docs/app/building-your-application/data-fetching#fetching-data-on-the-server) with Server Components. + +### Keeping Server-Only Code out of Client Components (Poisoning) + +Since JavaScript modules can be shared between both Server and Client Components, it's possible for code that was only ever intended to be run on the server to sneak its way into the client. + +For example, take the following data-fetching function: + +```ts filename="lib/data.ts" switcher +export async function getData() { + const res = await fetch('https://external-service.com/data', { + headers: { + authorization: process.env.API_KEY, + }, + }) + + return res.json() +} +``` + +```js filename="lib/data.js" switcher +export async function getData() { + const res = await fetch('https://external-service.com/data', { + headers: { + authorization: process.env.API_KEY, + }, + }) + + return res.json() +} +``` + +At first glance, it appears that `getData` works on both the server and the client. But because the environment variable `API_KEY` is not prefixed with `NEXT_PUBLIC`, it's a private variable that can only be accessed on the server. Next.js replaces private environment variables with the empty string in client code to prevent leaking secure information. + +As a result, even though `getData()` can be imported and executed on the client, it won't work as expected. And while making the variable public would make the function work on the client, it would leak sensitive information. + +So, this function was written with the intention that it would only ever be executed on the server. + +### The "server only" package + +To prevent this sort of unintended client usage of server code, we can use the `server-only` package to give other developers a build-time error if they ever accidentally import one of these modules into a Client Component. + +To use `server-only`, first install the package: + +```bash filename="Terminal" +npm install server-only +``` + +Then import the package into any module that contains server-only code: + +```js filename="lib/data.js" +import 'server-only' + +export async function getData() { + const res = await fetch('https://external-service.com/data', { + headers: { + authorization: process.env.API_KEY, + }, + }) + + return res.json() +} +``` + +Now, any Client Component that imports `getData()` will receive a build-time error explaining that this module can only be used on the server. + +The corresponding package `client-only` can be used to mark modules that contain client-only code – for example, code that accesses the `window` object. + +### Data Fetching + +Although it's possible to fetch data in Client Components, we recommend fetching data in Server Components unless you have a specific reason for fetching data on the client. Moving data fetching to the server leads to better performance and user experience. + +[Learn more about data fetching](/docs/app/building-your-application/data-fetching). + +### Third-party packages + +Since Server Components are new, third-party packages in the ecosystem are just beginning to add the `"use client"` directive to components that use client-only features like `useState`, `useEffect`, and `createContext`. + +Today, many components from `npm` packages that use client-only features do not yet have the directive. These third-party components will work as expected within your own [Client Components](#the-use-client-directive) since they have the `"use client"` directive, but they won't work within Server Components. + +For example, let's say you've installed the hypothetical `acme-carousel` package which has an `` component. This component uses `useState`, but it doesn't yet have the `"use client"` directive. + +If you use `` within a Client Component, it will work as expected: + +```tsx filename="app/gallery.tsx" switcher +'use client' + +import { useState } from 'react' +import { Carousel } from 'acme-carousel' + +export default function Gallery() { + let [isOpen, setIsOpen] = useState(false) + + return ( +
+ + + {/* Works, since Carousel is used within a Client Component */} + {isOpen && } +
+ ) +} +``` + +```jsx filename="app/gallery.js" switcher +'use client' + +import { useState } from 'react' +import { Carousel } from 'acme-carousel' + +export default function Gallery() { + let [isOpen, setIsOpen] = useState(false) + + return ( +
+ + + {/* Works, since Carousel is used within a Client Component */} + {isOpen && } +
+ ) +} +``` + +However, if you try to use it directly within a Server Component, you'll see an error: + +```tsx filename="app/page.tsx" switcher +import { Carousel } from 'acme-carousel' + +export default function Page() { + return ( +
+

View pictures

+ + {/* Error: `useState` can not be used within Server Components */} + +
+ ) +} +``` + +```jsx filename="app/page.js" switcher +import { Carousel } from 'acme-carousel' + +export default function Page() { + return ( +
+

View pictures

+ + {/* Error: `useState` can not be used within Server Components */} + +
+ ) +} +``` + +This is because Next.js doesn't know `` is using client-only features. + +To fix this, you can wrap third-party components that rely on client-only features in your own Client Components: + +```tsx filename="app/carousel.tsx" switcher +'use client' + +import { Carousel } from 'acme-carousel' + +export default Carousel +``` + +```jsx filename="app/carousel.js" switcher +'use client' + +import { Carousel } from 'acme-carousel' + +export default Carousel +``` + +Now, you can use `` directly within a Server Component: + +```tsx filename="app/page.tsx" switcher +import Carousel from './carousel' + +export default function Page() { + return ( +
+

View pictures

+ + {/* Works, since Carousel is a Client Component */} + +
+ ) +} +``` + +```jsx filename="app/page.js" switcher +import Carousel from './carousel' + +export default function Page() { + return ( +
+

View pictures

+ + {/* Works, since Carousel is a Client Component */} + +
+ ) +} +``` + +We don't expect you to need to wrap most third-party components since it's likely you'll be using them within Client Components. However, one exception is provider components, since they rely on React state and context, and are typically needed at the root of an application. [Learn more about third-party context providers below](#rendering-third-party-context-providers-in-server-components). + +#### Library Authors + +- In a similar fashion, library authors creating packages to be consumed by other developers can use the `"use client"` directive to mark client entry points of their package. This allows users of the package to import package components directly into their Server Components without having to create a wrapping boundary. +- You can optimize your package by using ['use client' deeper in the tree](#moving-client-components-to-the-leaves), allowing the imported modules to be part of the Server Component module graph. +- It's worth noting some bundlers might strip out `"use client"` directives. You can find an example of how to configure esbuild to include the `"use client"` directive in the [React Wrap Balancer](https://github.com/shuding/react-wrap-balancer/blob/main/tsup.config.ts#L10-L13) and [Vercel Analytics](https://github.com/vercel/analytics/blob/main/packages/web/tsup.config.js#L26-L30) repositories. + +## Context + +Most React applications rely on [context](https://react.dev/reference/react/useContext) to share data between components, either directly via [`createContext`](https://react.dev/reference/react/useContext), or indirectly via provider components imported from third-party libraries. + +In Next.js 13, context is fully supported within Client Components, but it **cannot** be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated. + +We'll discuss alternatives for sharing data between Server Components, but first, let's take a look at how to use context within Client Components. + +### Using context in Client Components + +All of the context APIs are fully supported within Client Components: + +```tsx filename="app/sidebar.tsx" switcher +'use client' + +import { createContext, useContext, useState } from 'react' + +const SidebarContext = createContext() + +export function Sidebar() { + const [isOpen, setIsOpen] = useState() + + return ( + + + + ) +} + +function SidebarNav() { + let { isOpen } = useContext(SidebarContext) + + return ( +
+

Home

+ + {isOpen && } +
+ ) +} +``` + +```jsx filename="app/sidebar.js" switcher +'use client' + +import { createContext, useContext, useState } from 'react' + +const SidebarContext = createContext() + +export function Sidebar() { + const [isOpen, setIsOpen] = useState() + + return ( + + + + ) +} + +function SidebarNav() { + let { isOpen } = useContext(SidebarContext) + + return ( +
+

Home

+ + {isOpen && } +
+ ) +} +``` + +However, context providers are typically rendered near the root of an application to share global concerns, like the current theme. Because context is not supported in Server Components, trying to create a context at the root of your application will cause an error: + +```tsx filename="app/layout.tsx" switcher +import { createContext } from 'react' + +// createContext is not supported in Server Components +export const ThemeContext = createContext({}) + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { createContext } from 'react' + +// createContext is not supported in Server Components +export const ThemeContext = createContext({}) + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +To fix this, create your context and render its provider inside of a Client Component: + +```tsx filename="app/theme-provider.tsx" switcher +'use client' + +import { createContext } from 'react' + +export const ThemeContext = createContext({}) + +export default function ThemeProvider({ children }) { + return {children} +} +``` + +```jsx filename="app/theme-provider.js" switcher +'use client' + +import { createContext } from 'react' + +export const ThemeContext = createContext({}) + +export default function ThemeProvider({ children }) { + return {children} +} +``` + +Your Server Component will now be able to directly render your provider since it's been marked as a Client Component: + +```tsx filename="app/layout.tsx" switcher +import ThemeProvider from './theme-provider' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import ThemeProvider from './theme-provider' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +With the provider rendered at the root, all other Client Components throughout your app will be able to consume this context. + +> **Good to know**: You should render providers as deep as possible in the tree – notice how `ThemeProvider` only wraps `{children}` instead of the entire `` document. This makes it easier for Next.js to optimize the static parts of your Server Components. + +### Rendering third-party context providers in Server Components + +Third-party npm packages often include Providers that need to be rendered near the root of your application. If these providers include the `"use client"` directive, they can be rendered directly inside of your Server Components. However, since Server Components are so new, many third-party providers won't have added the directive yet. + +If you try to render a third-party provider that doesn't have `"use client"`, it will cause an error: + +```tsx filename="app/layout.tsx" switcher +import { ThemeProvider } from 'acme-theme' + +export default function RootLayout({ children }) { + return ( + + + {/* Error: `createContext` can't be used in Server Components */} + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { ThemeProvider } from 'acme-theme' + +export default function RootLayout({ children }) { + return ( + + + {/* Error: `createContext` can't be used in Server Components */} + {children} + + + ) +} +``` + +To fix this, wrap third-party providers in your own Client Component: + +```jsx filename="app/providers.js" +'use client' + +import { ThemeProvider } from 'acme-theme' +import { AuthProvider } from 'acme-auth' + +export function Providers({ children }) { + return ( + + {children} + + ) +} +``` + +Now, you can import and render `` directly within your root layout. + +```jsx filename="app/layout.js" +import { Providers } from './providers' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +With the providers rendered at the root, all the components and hooks from these libraries will work as expected within your own Client Components. + +Once a third-party library has added `"use client"` to its client code, you'll be able to remove the wrapper Client Component. + +### Sharing data between Server Components + +Since Server Components are not interactive and therefore do not read from React state, you don't need React context to share data. Instead, you can use native JavaScript patterns for common data that multiple Server Components need to access. For example, a module can be used to share a database connection across multiple components: + +```ts filename="utils/database.ts" switcher +export const db = new DatabaseConnection() +``` + +```js filename="utils/database.js" switcher +export const db = new DatabaseConnection() +``` + +```tsx filename="app/users/layout.tsx" switcher +import { db } from '@utils/database' + +export async function UsersLayout() { + let users = await db.query() + // ... +} +``` + +```jsx filename="app/users/layout.js" switcher +import { db } from '@utils/database' + +export async function UsersLayout() { + let users = await db.query() + // ... +} +``` + +```tsx filename="app/users/[id]/page.tsx" switcher +import { db } from '@utils/database' + +export async function DashboardPage() { + let user = await db.query() + // ... +} +``` + +```jsx filename="app/users/[id]/page.js" switcher +import { db } from '@utils/database' + +export async function DashboardPage() { + let user = await db.query() + // ... +} +``` + +In the above example, both the layout and page need to make database queries. Each of these components shares access to the database by importing the `@utils/database` module. This JavaScript pattern is called global singletons. + +### Sharing fetch requests between Server Components + +When fetching data, you may want to share the result of a `fetch` between a `page` or `layout` and some of its children components. This is an unnecessary coupling between the components and can lead to passing `props` back and forth between components. + +Instead, we recommend colocating data fetching alongside the component that consumes the data. [`fetch` requests are automatically deduped](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) in Server Components, so each route segment can request exactly the data it needs without worrying about duplicate requests. Next.js will read the same value from the `fetch` cache. diff --git a/docs/01-getting-started/index.mdx b/docs/01-getting-started/index.mdx new file mode 100644 index 0000000000000..2c04700d7b5b8 --- /dev/null +++ b/docs/01-getting-started/index.mdx @@ -0,0 +1,8 @@ +--- +title: Getting Started +description: Learn how to create full-stack web applications with Next.js. +--- + +Get started with Next.js by creating a new application using the [Installation](/docs/getting-started/installation) guide. + +If you are new to React, we recommend going through the [React Essentials](/docs/getting-started/react-essentials) guides for an introduction of the basic concepts. diff --git a/docs/02-app/01-building-your-application/01-routing/01-defining-routes.mdx b/docs/02-app/01-building-your-application/01-routing/01-defining-routes.mdx new file mode 100644 index 0000000000000..7cefd3d1d29f6 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/01-defining-routes.mdx @@ -0,0 +1,58 @@ +--- +title: Defining Routes +description: Learn how to create your first route in Next.js. +related: + description: Learn more about creating pages and layouts. + links: + - app/building-your-application/routing/pages-and-layouts +--- + +> We recommend reading the [Routing Fundamentals](/docs/app/building-your-application/routing) page before continuing. + +This page will guide you through how to define and organize routes in your Next.js application. + +## Creating Routes + +Next.js uses a file-system based router where **folders** are used to define routes. + +Each folder represents a [**route** segment](/docs/app/building-your-application/routing#route-segments) that maps to a **URL** segment. To create a [nested route](/docs/app/building-your-application/routing#nested-routes), you can nest folders inside each other. + +Route segments to path segments + +A special [`page.js` file](/docs/app/building-your-application/routing/pages-and-layouts#pages) is used to make route segments publicly accessible. + +Defining Routes + +In this example, the `/dashboard/analytics` URL path is _not_ publicly accessible because it does not have a corresponding `page.js` file. This folder could be used to store components, stylesheets, images, or other colocated files. + +> **Good to know**: `.js`, `.jsx`, or `.tsx` file extensions can be used for special files. + +## Creating UI + +[Special file conventions](/docs/app/building-your-application/routing#file-conventions) are used to create UI for each route segment. The most common are [pages](/docs/app/building-your-application/routing/pages-and-layouts#pages) to show UI unique to a route, and [layouts](/docs/app/building-your-application/routing/pages-and-layouts#layouts) to show UI that is shared across multiple routes. + +For example, to create your first page, add a `page.js` file inside the `app` directory and export a React component: + +```tsx filename="app/page.tsx" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` + +```jsx filename="app/page.js" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` diff --git a/docs/02-app/01-building-your-application/01-routing/02-pages-and-layouts.mdx b/docs/02-app/01-building-your-application/01-routing/02-pages-and-layouts.mdx new file mode 100644 index 0000000000000..dd8c88fea2f71 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/02-pages-and-layouts.mdx @@ -0,0 +1,272 @@ +--- +title: Pages and Layouts +description: Create your first page and shared layout with the App Router. +--- + +> We recommend reading the [Routing Fundamentals](/docs/app/building-your-application/routing) and [Defining Routes](/docs/app/building-your-application/routing/defining-routes) pages before continuing. + +The App Router inside Next.js 13 introduced new file conventions to easily create [pages](#pages), [shared layouts](#layouts), and [templates](#templates). This page will guide you through how to use these special files in your Next.js application. + +## Pages + +A page is UI that is **unique** to a route. You can define pages by exporting a component from a `page.js` file. Use nested folders to [define a route](/docs/app/building-your-application/routing/defining-routes) and a `page.js` file to make the route publicly accessible. + +Create your first page by adding a `page.js` file inside the `app` directory: + +page.js special file + +```tsx filename="app/page.tsx" switcher +// `app/page.tsx` is the UI for the `/` URL +export default function Page() { + return

Hello, Home page!

+} +``` + +```jsx filename="app/page.js" switcher +// `app/page.js` is the UI for the `/` URL +export default function Page() { + return

Hello, Home page!

+} +``` + +```tsx filename="app/dashboard/page.tsx" switcher +// `app/dashboard/page.tsx` is the UI for the `/dashboard` URL +export default function Page() { + return

Hello, Dashboard Page!

+} +``` + +```jsx filename="app/dashboard/page.js" switcher +// `app/dashboard/page.js` is the UI for the `/dashboard` URL +export default function Page() { + return

Hello, Dashboard Page!

+} +``` + +> **Good to know**: +> +> - A page is always the [leaf](/docs/app/building-your-application/routing#terminology) of the [route subtree](/docs/app/building-your-application/routing#terminology). +> - `.js`, `.jsx`, or `.tsx` file extensions can be used for Pages. +> - A `page.js` file is required to make a route segment publicly accessible. +> - Pages are [Server Components](/docs/getting-started/react-essentials) by default but can be set to a [Client Component](/docs/getting-started/react-essentials#client-components). +> - Pages can fetch data. View the [Data Fetching](/docs/app/building-your-application/data-fetching) section for more information. + +## Layouts + +A layout is UI that is **shared** between multiple pages. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be [nested](#nesting-layouts). + +You can define a layout by `default` exporting a React component from a `layout.js` file. The component should accept a `children` prop that will be populated with a child layout (if it exists) or a child page during rendering. + +layout.js special file + +```tsx filename="app/dashboard/layout.tsx" switcher +export default function DashboardLayout({ + children, // will be a page or nested layout +}: { + children: React.ReactNode +}) { + return ( +
+ {/* Include shared UI here e.g. a header or sidebar */} + + + {children} +
+ ) +} +``` + +```jsx filename="app/dashboard/layout.js" switcher +export default function DashboardLayout({ + children, // will be a page or nested layout +}) { + return ( +
+ {/* Include shared UI here e.g. a header or sidebar */} + + + {children} +
+ ) +} +``` + +> **Good to know**: +> +> - The top-most layout is called the [Root Layout](#root-layout-required). This **required** layout is shared across all pages in an application. Root layouts must contain `html` and `body` tags. +> - Any route segment can optionally define its own [Layout](#nesting-layouts). These layouts will be shared across all pages in that segment. +> - Layouts in a route are **nested** by default. Each parent layout wraps child layouts below it using the React `children` prop. +> - You can use [Route Groups](/docs/app/building-your-application/routing/route-groups) to opt specific route segments in and out of shared layouts. +> - Layouts are [Server Components](/docs/getting-started/react-essentials) by default but can be set to a [Client Component](/docs/getting-started/react-essentials#client-components). +> - Layouts can fetch data. View the [Data Fetching](/docs/app/building-your-application/data-fetching) section for more information. +> - Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will [automatically dedupe the requests](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) without affecting performance. +> - Layouts do not have access to the current route segment(s). To access route segments, you can use [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment) or [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments) in a Client Component. +> - `.js`, `.jsx`, or `.tsx` file extensions can be used for Layouts. +> - A `layout.js` and `page.js` file can be defined in the same folder. The layout will wrap the page. + +### Root Layout (Required) + +The root layout is defined at the top level of the `app` directory and applies to all routes. This layout enables you to modify the initial HTML returned from the server. + +```tsx filename="app/layout.tsx" switcher +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +> **Good to know**: +> +> - The `app` directory **must** include a root layout. +> - The root layout must define `` and `` tags since Next.js does not automatically create them. +> - You can use the [built-in SEO support](/docs/app/building-your-application/optimizing/metadata) to manage `` HTML elements, for example, the `` element. +> - You can use [route groups](/docs/app/building-your-application/routing/route-groups) to create multiple root layouts. See an [example here](/docs/app/building-your-application/routing/route-groups#creating-multiple-root-layouts). +> - The root layout is a [Server Component](/docs/getting-started/react-essentials) by default and **can not** be set to a [Client Component](/docs/getting-started/react-essentials#client-components). + +> **Migrating from the `pages` directory:** The root layout replaces the [`_app.js`](/docs/pages/building-your-application/routing/custom-app) and [`_document.js`](/docs/pages/building-your-application/routing/custom-document) files. [View the migration guide](/docs/app/building-your-application/upgrading/app-router-migration#migrating-_documentjs-and-_appjs). + +### Nesting Layouts + +Layouts defined inside a folder (e.g. `app/dashboard/layout.js`) apply to specific route segments (e.g. `acme.com/dashboard`) and render when those segments are active. By default, layouts in the file hierarchy are **nested**, which means they wrap child layouts via their `children` prop. + +<Image + alt="Nested Layout" + srcLight="/docs/light/nested-layout.png" + srcDark="/docs/dark/nested-layout.png" + width="1600" + height="606" +/> + +```tsx filename="app/dashboard/layout.tsx" switcher +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return <section>{children}</section> +} +``` + +```jsx filename="app/dashboard/layout.js" switcher +export default function DashboardLayout({ children }) { + return <section>{children}</section> +} +``` + +If you were to combine the two layouts above, the root layout (`app/layout.js`) would wrap the dashboard layout (`app/dashboard/layout.js`), which would wrap route segments inside `app/dashboard/*`. + +The two layouts would be nested as such: + +<Image + alt="Nested Layouts" + srcLight="/docs/light/nested-layouts-ui.png" + srcDark="/docs/dark/nested-layouts-ui.png" + width="1600" + height="1026" +/> + +You can use [Route Groups](/docs/app/building-your-application/routing/route-groups) to opt specific route segments in and out of shared layouts. + +## Templates + +Templates are similar to layouts in that they wrap each child layout or page. Unlike layouts that persist across routes and maintain state, templates create a new instance for each of their children on navigation. This means that when a user navigates between routes that share a template, a new instance of the component is mounted, DOM elements are recreated, state is **not** preserved, and effects are re-synchronized. + +There may be cases where you need those specific behaviors, and templates would be a more suitable option than layouts. For example: + +- Enter/exit animations using CSS or animation libraries. +- Features that rely on `useEffect` (e.g logging page views) and `useState` (e.g a per-page feedback form). +- To change the default framework behavior. For example, Suspense Boundaries inside layouts only show the fallback the first time the Layout is loaded and not when switching pages. For templates, the fallback is shown on each navigation. + +> **Recommendation:** We recommend using Layouts unless you have a specific reason to use Template. + +A template can be defined by exporting a default React component from a `template.js` file. The component should accept a `children` prop which will be nested segments. + +<Image + alt="template.js special file" + srcLight="/docs/light/template-special-file.png" + srcDark="/docs/dark/template-special-file.png" + width="1600" + height="444" +/> + +```tsx filename="app/template.tsx" switcher +export default function Template({ children }: { children: React.ReactNode }) { + return <div>{children}</div> +} +``` + +```jsx filename="app/template.js" switcher +export default function Template({ children }) { + return <div>{children}</div> +} +``` + +The rendered output of a route segment with a layout and a template will be as such: + +```jsx filename="Output" +<Layout> + {/* Note that the template is given a unique key. */} + <Template key={routeParam}>{children}</Template> +</Layout> +``` + +## Modifying `<head>` + +In the `app` directory, you can modify the `<head>` HTML elements such as `title` and `meta` using the [built-in SEO support](/docs/app/building-your-application/optimizing/metadata). + +Metadata can be defined by exporting a [`metadata` object](/docs/app/api-reference/functions/generate-metadata#the-metadata-object) or [`generateMetadata` function](/docs/app/api-reference/functions/generate-metadata#generatemetadata-function) in a [`layout.js`](/docs/app/api-reference/file-conventions/layout) or [`page.js`](/docs/app/api-reference/file-conventions/page) file. + +```tsx filename="app/page.tsx" switcher +import { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Next.js', +} + +export default function Page() { + return '...' +} +``` + +```jsx filename="app/page.js" switcher +export const metadata = { + title: 'Next.js', +} + +export default function Page() { + return '...' +} +``` + +> **Good to know**: You should **not** manually add `<head>` tags such as `<title>` and `<meta>` to root layouts. Instead, you should use the [Metadata API](/docs/app/api-reference/functions/generate-metadata) which automatically handles advanced requirements such as streaming and de-duplicating `<head>` elements. + +[Learn more about available metadata options in the API reference.](/docs/app/api-reference/functions/generate-metadata) diff --git a/docs/02-app/01-building-your-application/01-routing/03-linking-and-navigating.mdx b/docs/02-app/01-building-your-application/01-routing/03-linking-and-navigating.mdx new file mode 100644 index 0000000000000..4f0215d205874 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/03-linking-and-navigating.mdx @@ -0,0 +1,194 @@ +--- +title: Linking and Navigating +description: Learn how navigation works in Next.js, and how to use the Link Component and `useRouter` hook. +--- + +The Next.js router uses [server-centric routing](/docs/app/building-your-application/routing#server-centric-routing-with-client-side-navigation) with [client-side navigation](#how-navigation-works). It supports [instant loading states](/docs/app/building-your-application/routing/loading-ui-and-streaming) and [concurrent rendering](https://react.dev/reference/react/startTransition). This means navigation maintains client-side state, avoids expensive re-renders, is interruptible, and doesn't cause race conditions. + +There are two ways to navigate between routes: + +- [`<Link>` Component](#link-component) +- [`useRouter` Hook](#userouter-hook) + +This page will go through how to use `<Link>`, `useRouter()`, and dive deeper into how navigation works. + +## `<Link>` Component + +`<Link>` is a React component that extends the HTML `<a>` element to provide [prefetching](#prefetching) and client-side navigation between routes. It is the primary way to navigate between routes in Next.js. + +To use `<Link>`, import it from `next/link`, and pass a `href` prop to the component: + +```tsx filename="app/page.tsx" switcher +import Link from 'next/link' + +export default function Page() { + return <Link href="/dashboard">Dashboard</Link> +} +``` + +```jsx filename="app/page.js" switcher +import Link from 'next/link' + +export default function Page() { + return <Link href="/dashboard">Dashboard</Link> +} +``` + +There are optional props you can pass to `<Link>`. See the [API reference](/docs/app/api-reference/components/link) for more information. + +## Examples + +### Linking to Dynamic Segments + +When linking to [dynamic segments](/docs/app/building-your-application/routing/dynamic-routes), you can use [template literals and interpolation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) to generate a list of links. For example, to generate a list of blog posts: + +```jsx filename="app/blog/PostList.js" +import Link from 'next/link' + +export default function PostList({ posts }) { + return ( + <ul> + {posts.map((post) => ( + <li key={post.id}> + <Link href={`/blog/${post.slug}`}>{post.title}</Link> + </li> + ))} + </ul> + ) +} +``` + +### Checking Active Links + +You can use [`usePathname()`](/docs/app/api-reference/functions/use-pathname) to determine if a link is active. For example, to add a class to the active link, you can check if the current `pathname` matches the `href` of the link: + +```jsx filename="app/ui/Navigation.js" +'use client' + +import { usePathname } from 'next/navigation' +import { Link } from 'next/link' + +export function Navigation({ navLinks }) { + const pathname = usePathname() + + return ( + <> + {navLinks.map((link) => { + const isActive = pathname.startsWith(link.href) + + return ( + <Link + className={isActive ? 'text-blue' : 'text-black'} + href={link.href} + key={link.name} + > + {link.name} + </Link> + ) + })} + </> + ) +} +``` + +### Scrolling to an `id` + +The default behavior of `<Link>` is to [scroll to the top of the route segment that has changed](#focus-and-scroll-management). When there is an `id` defined in `href`, it will scroll to the specific `id`, similarly to a normal `<a>` tag. + +To prevent scrolling to the top of the route segment, set `scroll={false}` and add a hashed `id` to `href`: + +```jsx +<Link href="/#hashid" scroll={false}> + Scroll to specific id. +</Link> +``` + +## `useRouter()` Hook + +The `useRouter` hook allows you to programmatically change routes inside [Client Components](/docs/getting-started/react-essentials). + +To use `useRouter`, import it from `next/navigation`, and call the hook inside your Client Component: + +```jsx filename="app/page.js" +'use client' + +import { useRouter } from 'next/navigation' + +export default function Page() { + const router = useRouter() + + return ( + <button type="button" onClick={() => router.push('/dashboard')}> + Dashboard + </button> + ) +} +``` + +The `useRouter` provides methods such as `push()`, `refresh()`, and more. See the [API reference](/docs/app/api-reference/functions/use-router) for more information. + +> **Recommendation:** Use the `<Link>` component to navigate between routes unless you have a specific requirement for using `useRouter`. + +## How Navigation Works + +- A route transition is initiated using `<Link>` or calling `router.push()`. +- The router updates the URL in the browser's address bar. +- The router avoids unnecessary work by re-using segments that haven't changed (e.g. shared layouts) from the [client-side cache](#client-side-caching-of-rendered-server-components). This is also referred to as [partial rendering](/docs/app/building-your-application/routing#partial-rendering). +- If the [conditions of soft navigation](#conditions-for-soft-navigation) are met, the router fetches the new segment from the cache rather than the server. If not, the router performs a [hard navigation](#hard-navigation) and fetches the Server Component payload from the server. +- If created, [loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming) is shown from the server while the payload is being fetched. +- The router uses the cached or fresh payload to render the new segments on the client. + +### Client-side Caching of Rendered Server Components + +> **Good to know**: This client-side cache is different from the server-side [Next.js HTTP cache](/docs/app/building-your-application/data-fetching#caching-data). + +The new router has an **in-memory client-side cache** that stores the **rendered result** of Server Components (payload). The cache is split by route segments which allows invalidation at any level and ensures consistency across concurrent renders. + +As users navigate around the app, the router will store the payload of previously fetched segments **and** [prefetched](#prefetching) segments in the cache. + +This means, for certain cases, the router can re-use the cache instead of making a new request to the server. This improves performance by avoiding re-fetching data and re-rendering components unnecessarily. + +### Invalidating the Cache + +[Server Actions](/docs/app/building-your-application/data-fetching/server-actions) can be used to revalidate data on-demand by path ([`revalidatePath`](/docs/app/api-reference/functions/revalidatePath)) or by cache tag ([`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)). + +### Prefetching + +Prefetching is a way to preload a route in the background before it's visited. The rendered result of prefetched routes is added to the router's client-side cache. This makes navigating to a prefetched route near-instant. + +By default, routes are prefetched as they become visible in the viewport when using the `<Link>` component. This can happen when the page first loads or through scrolling. Routes can also be programmatically prefetched using the `prefetch` method of the [`useRouter()` hook](/docs/app/api-reference/functions/use-router#userouter). + +**Static and Dynamic Routes**: + +- If the route is static, all the Server Component payloads for the route segments will be prefetched. +- If the route is dynamic, the payload from the first shared layout down until the first `loading.js` file is prefetched. This reduces the cost of prefetching the whole route dynamically and allows [instant loading states](/docs/app/building-your-application/routing/loading-ui-and-streaming#instant-loading-states) for dynamic routes. + +> **Good to know**: +> +> - Prefetching is only enabled in production. +> - Prefetching can be disabled by passing `prefetch={false}` to `<Link>`. + +### Soft Navigation + +On navigation, the cache for changed segments is reused (if it exists), and no new requests are made to the server for data. + +#### Conditions for Soft Navigation + +On navigation, Next.js will use soft navigation if the route you are navigating to has been [**prefetched**](#prefetching), and either doesn't include [dynamic segments](/docs/app/building-your-application/routing/dynamic-routes) **or** has the same dynamic parameters as the current route. + +For example, consider the following route that includes a dynamic `[team]` segment: `/dashboard/[team]/*`. The cached segments below `/dashboard/[team]/*` will only be invalidated when the `[team]` parameter changes. + +- Navigating from `/dashboard/team-red/*` to `/dashboard/team-red/*` will be a soft navigation. +- Navigating from `/dashboard/team-red/*` to `/dashboard/team-blue/*` will be a hard navigation. + +### Hard Navigation + +On navigation, the cache is invalidated and the server refetches data and re-renders the changed segments. + +### Back/Forward Navigation + +Back and forward navigation ([popstate event](https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event)) has a soft navigation behavior. This means, the client-side cache is re-used and navigation is near-instant. + +### Focus and Scroll Management + +By default, Next.js will set focus and scroll into view the segment that's changed on navigation. diff --git a/docs/02-app/01-building-your-application/01-routing/04-route-groups.mdx b/docs/02-app/01-building-your-application/01-routing/04-route-groups.mdx new file mode 100644 index 0000000000000..d0e11b5fb0bca --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/04-route-groups.mdx @@ -0,0 +1,78 @@ +--- +title: Route Groups +description: Route Groups can be used to partition your Next.js application into different sections. +--- + +In the `app` directory, nested folders are normally mapped to URL paths. However, you can mark a folder as a **Route Group** to prevent the folder from being included in the route's URL path. + +This allows you to organize your route segments and project files into logical groups without affecting the URL path structure. + +Route groups are useful for: + +- [Organizing routes into groups](#organize-routes-without-affecting-the-url-path) e.g. by site section, intent, or team. +- Enabling [nested layouts](/docs/app/building-your-application/routing/pages-and-layouts) in the same route segment level: + - [Creating multiple nested layouts in the same segment, including multiple root layouts](#creating-multiple-root-layouts) + - [Adding a layout to a subset of routes in a common segment](#opting-specific-segments-into-a-layout) + +## Convention + +A route group can be created by wrapping a folder's name in parenthesis: `(folderName)` + +## Examples + +### Organize routes without affecting the URL path + +To organize routes without affecting the URL, create a group to keep related routes together. The folders in parenthesis will be omitted from the URL (e.g. `(marketing)` or `(shop)`). + +<Image + alt="Organizing Routes with Route Groups" + srcLight="/docs/light/route-group-organisation.png" + srcDark="/docs/dark/route-group-organisation.png" + width="1600" + height="930" +/> + +Even though routes inside `(marketing)` and `(shop)` share the same URL hierarchy, you can create a different layout for each group by adding a `layout.js` file inside their folders. + +<Image + alt="Route Groups with Multiple Layouts" + srcLight="/docs/light/route-group-multiple-layouts.png" + srcDark="/docs/dark/route-group-multiple-layouts.png" + width="1600" + height="768" +/> + +### Opting specific segments into a layout + +To opt specific routes into a layout, create a new route group (e.g. `(shop)`) and move the routes that share the same layout into the group (e.g. `account` and `cart`). The routes outside of the group will not share the layout (e.g. `checkout`). + +<Image + alt="Route Groups with Opt-in Layouts" + srcLight="/docs/light/route-group-opt-in-layouts.png" + srcDark="/docs/dark/route-group-opt-in-layouts.png" + width="1600" + height="930" +/> + +### Creating multiple root layouts + +To create multiple [root layouts](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required), remove the top-level `layout.js` file, and add a `layout.js` file inside each route groups. This is useful for partitioning an application into sections that have a completely different UI or experience. The `<html>` and `<body>` tags need to be added to each root layout. + +<Image + alt="Route Groups with Multiple Root Layouts" + srcLight="/docs/light/route-group-multiple-root-layouts.png" + srcDark="/docs/dark/route-group-multiple-root-layouts.png" + width="1600" + height="687" +/> + +In the example above, both `(marketing)` and `(shop)` have their own root layout. + +--- + +> **Good to know**: +> +> - The naming of route groups has no special significance other than for organization. They do not affect the URL path. +> - Routes that include a route group **should not** resolve to the same URL path as other routes. For example, since route groups don't affect URL structure, `(marketing)/about/page.js` and `(shop)/about/page.js` would both resolve to `/about` and cause an error. +> - If you use multiple root layouts without a top-level `layout.js` file, your home `page.js` file should be defined in one of the route groups, For example: `app/(marketing)/page.js`. +> - Navigating **across multiple root layouts** will cause a **full page load** (as opposed to a client-side navigation). For example, navigating from `/cart` that uses `app/(shop)/layout.js` to `/blog` that uses `app/(marketing)/layout.js` will cause a full page load. This **only** applies to multiple root layouts. diff --git a/docs/02-app/01-building-your-application/01-routing/05-dynamic-routes.mdx b/docs/02-app/01-building-your-application/01-routing/05-dynamic-routes.mdx new file mode 100644 index 0000000000000..a8216617c1821 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/05-dynamic-routes.mdx @@ -0,0 +1,125 @@ +--- +title: Dynamic Routes +description: Dynamic Routes can be used to programmatically generate route segments from dynamic data. +related: + title: Next Steps + description: For more information on what to do next, we recommend the following sections + links: + - app/building-your-application/routing/linking-and-navigating + - app/api-reference/functions/generate-static-params +--- + +When you don't know the exact segment names ahead of time and want to create routes from dynamic data, you can use Dynamic Segments that are filled in at request time or [prerendered](#generating-static-params) at build time. + +## Convention + +A Dynamic Segment can be created by wrapping a folder's name in square brackets: `[folderName]`. For example, `[id]` or `[slug]`. + +Dynamic Segments are passed as the `params` prop to [`layout`](/docs/app/api-reference/file-conventions/layout), [`page`](/docs/app/api-reference/file-conventions/page), [`route`](/docs/app/building-your-application/routing/router-handlers), and [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata#generatemetadata-function) functions. + +## Example + +For example, a blog could include the following route `app/blog/[slug]/page.js` where `[slug]` is the Dynamic Segment for blog posts. + +```tsx filename="app/blog/[slug]/page.tsx" switcher +export default function Page({ params }: { params: { slug: string } }) { + return <div>My Post: {params.slug}</div> +} +``` + +```jsx filename="app/blog/[slug]/page.js" switcher +export default function Page({ params }) { + return <div>My Post: {params.slug}</div> +} +``` + +| Route | Example URL | `params` | +| ------------------------- | ----------- | --------------- | +| `app/blog/[slug]/page.js` | `/blog/a` | `{ slug: 'a' }` | +| `app/blog/[slug]/page.js` | `/blog/b` | `{ slug: 'b' }` | +| `app/blog/[slug]/page.js` | `/blog/c` | `{ slug: 'c' }` | + +See the [generateStaticParams()](#generating-static-params) page to learn how to generate the params for the segment. + +> **Good to know**: Dynamic Segments are equivalent to [Dynamic Routes](/docs/app/building-your-application/routing/dynamic-routes) in the `pages` directory. + +## Generating Static Params + +The `generateStaticParams` function can be used in combination with [dynamic route segments](/docs/app/building-your-application/routing/dynamic-routes) to [**statically generate**](/docs/app/building-your-application/rendering/static-and-dynamic-rendering#static-rendering-default) routes at build time instead of on-demand at request time. + +```tsx filename="app/blog/[slug]/page.tsx" switcher +export async function generateStaticParams() { + const posts = await fetch('https://.../posts').then((res) => res.json()) + + return posts.map((post) => ({ + slug: post.slug, + })) +} +``` + +```jsx filename="app/blog/[slug]/page.js" switcher +export async function generateStaticParams() { + const posts = await fetch('https://.../posts').then((res) => res.json()) + + return posts.map((post) => ({ + slug: post.slug, + })) +} +``` + +The primary benefit of the `generateStaticParams` function is its smart retrieval of data. If content is fetched within the `generateStaticParams` function using a `fetch` request, the requests are [automatically deduplicated](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping). This means a `fetch` request with the same arguments across multiple `generateStaticParams`, Layouts, and Pages will only be made once, which decreases build times. + +Use the [migration guide](/docs/app/building-your-application/upgrading/app-router-migration#dynamic-paths-getstaticpaths) if you are migrating from the `pages` directory. + +See [`generateStaticParams` server function documentation](/docs/app/api-reference/functions/generate-static-params) for more information and advanced use cases. + +## Catch-all Segments + +Dynamic Segments can be extended to **catch-all** subsequent segments by adding an ellipsis inside the brackets `[...folderName]`. + +For example, `app/shop/[...slug]/page.js` will match `/shop/clothes`, but also `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`, and so on. + +| Route | Example URL | `params` | +| ---------------------------- | ------------- | --------------------------- | +| `app/shop/[...slug]/page.js` | `/shop/a` | `{ slug: ['a'] }` | +| `app/shop/[...slug]/page.js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` | +| `app/shop/[...slug]/page.js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` | + +## Optional Catch-all Segments + +Catch-all Segments can be made **optional** by including the parameter in double square brackets: `[[...folderName]]`. + +For example, `app/shop/[[...slug]]/page.js` will **also** match `/shop`, in addition to `/shop/clothes`, `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`. + +The difference between **catch-all** and **optional catch-all** segments is that with optional, the route without the parameter is also matched (`/shop` in the example above). + +| Route | Example URL | `params` | +| ------------------------------ | ------------- | --------------------------- | +| `app/shop/[[...slug]]/page.js` | `/shop` | `{}` | +| `app/shop/[[...slug]]/page.js` | `/shop/a` | `{ slug: ['a'] }` | +| `app/shop/[[...slug]]/page.js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` | +| `app/shop/[[...slug]]/page.js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` | + +## TypeScript + +When using TypeScript, you can add types for `params` depending on your configured route segment. + +```tsx filename="app/blog/[slug]/page.tsx" switcher +export default function Page({ params }: { params: { slug: string } }) { + return <h1>My Page</h1> +} +``` + +```jsx filename="app/blog/[slug]/page.js" switcher +export default function Page({ params }) { + return <h1>My Page</h1> +} +``` + +| Route | `params` Type Definition | +| ----------------------------------- | ---------------------------------------- | +| `app/blog/[slug]/page.js` | `{ slug: string }` | +| `app/shop/[...slug]/page.js` | `{ slug: string[] }` | +| `app/[categoryId]/[itemId]/page.js` | `{ categoryId: string, itemId: string }` | + +> **Good to know**: This may be done automatically by the [TypeScript plugin](/docs/app/building-your-application/configuring/typescript#typescript-plugin) in the future. diff --git a/docs/02-app/01-building-your-application/01-routing/06-loading-ui-and-streaming.mdx b/docs/02-app/01-building-your-application/01-routing/06-loading-ui-and-streaming.mdx new file mode 100644 index 0000000000000..2d070cd1c57ec --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/06-loading-ui-and-streaming.mdx @@ -0,0 +1,174 @@ +--- +title: Loading UI and Streaming +description: Built on top of Suspense, Loading UI allows you to create a fallback for specific route segments, and automatically stream content as it becomes ready. +--- + +The special file `loading.js` helps you create meaningful Loading UI with [React Suspense](https://react.dev/reference/react/Suspense). With this convention, you can show an [instant loading state](#instant-loading-states) from the server while the content of a route segment loads, the new content is automatically swapped in once rendering is complete. + +<Image + alt="Loading UI" + srcLight="/docs/light/loading-ui.png" + srcDark="/docs/dark/loading-ui.png" + width="1600" + height="691" +/> + +## Instant Loading States + +An instant loading state is fallback UI that is shown immediately upon navigation. You can pre-render loading indicators such as skeletons and spinners, or a small but meaningful part of future screens such as a cover photo, title, etc. This helps users understand the app is responding and provides a better user experience. + +Create a loading state by adding a `loading.js` file inside a folder. + +<Image + alt="loading.js special file" + srcLight="/docs/light/loading-special-file.png" + srcDark="/docs/dark/loading-special-file.png" + width="1600" + height="606" +/> + +```tsx filename="app/dashboard/loading.tsx" switcher +export default function Loading() { + // You can add any UI inside Loading, including a Skeleton. + return <LoadingSkeleton /> +} +``` + +```jsx filename="app/dashboard/loading.js" switcher +export default function Loading() { + // You can add any UI inside Loading, including a Skeleton. + return <LoadingSkeleton /> +} +``` + +In the same folder, `loading.js` will be nested inside `layout.js`. It will automatically wrap the `page.js` file and any children below in a `<Suspense>` boundary. + +<Image + alt="loading.js overview" + srcLight="/docs/light/loading-overview.png" + srcDark="/docs/dark/loading-overview.png" + width="1600" + height="768" +/> + +> **Good to know**: +> +> - Navigation is immediate, even with [server-centric routing](/docs/app/building-your-application/routing#server-centric-routing-with-client-side-navigation). +> - Navigation is interruptible, meaning changing routes does not need to wait for the content of the route to fully load before navigating to another route. +> - Shared layouts remain interactive while new route segments load. + +> **Recommendation:** Use the `loading.js` convention for route segments (layouts and pages) as Next.js optimizes this functionality. + +## Streaming with Suspense + +In addition to `loading.js`, you can also manually create Suspense Boundaries for your own UI components. The App Router supports streaming with [Suspense](https://react.dev/reference/react/Suspense) for both [Node.js and Edge runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). + +### What is Streaming? + +To learn how Streaming works in React and Next.js, it's helpful to understand **Server-Side Rendering (SSR)** and its limitations. + +With SSR, there's a series of steps that need to be completed before a user can see and interact with a page: + +1. First, all data for a given page is fetched on the server. +2. The server then renders the HTML for the page. +3. The HTML, CSS, and JavaScript for the page are sent to the client. +4. A non-interactive user interface is shown using the generated HTML, and CSS. +5. Finally, React [hydrates](https://react.dev/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) the user interface to make it interactive. + +<Image + alt="Chart showing Server Rendering without Streaming" + srcLight="/docs/light/server-rendering-without-streaming-chart.png" + srcDark="/docs/dark/server-rendering-without-streaming-chart.png" + width="1600" + height="612" +/> + +These steps are sequential and blocking, meaning the server can only render the HTML for a page once all the data has been fetched. And, on the client, React can only hydrate the UI once the code for all components in the page has been downloaded. + +SSR with React and Next.js helps improve the perceived loading performance by showing a non-interactive page to the user as soon as possible. + +<Image + alt="Server Rendering without Streaming" + srcLight="/docs/light/server-rendering-without-streaming.png" + srcDark="/docs/dark/server-rendering-without-streaming.png" + width="1600" + height="748" +/> + +However, it can still be slow as all data fetching on server needs to be completed before the page can be shown to the user. + +**Streaming** allows you to break down the page's HTML into smaller chunks and progressively send those chunks from the server to the client. + +<Image + alt="How Server Rendering with Streaming Works" + srcLight="/docs/light/server-rendering-with-streaming.png" + srcDark="/docs/dark/server-rendering-with-streaming.png" + width="1600" + height="785" +/> + +This enables parts of the page to be displayed sooner, without waiting for all the data to load before any UI can be rendered. + +Streaming works well with React's component model because each component can be considered a chunk. Components that have higher priority (e.g. product information) or that don't rely on data can be sent first (e.g. layout), and React can start hydration earlier. Components that have lower priority (e.g. reviews, related products) can be sent in the same server request after their data has been fetched. + +<Image + alt="Chart showing Server Rendering with Streaming" + srcLight="/docs/light/server-rendering-with-streaming-chart.png" + srcDark="/docs/dark/server-rendering-with-streaming-chart.png" + width="1600" + height="730" +/> + +Streaming is particularly beneficial when you want to prevent long data requests from blocking the page from rendering as it can reduce the [Time To First Byte (TTFB)](https://web.dev/ttfb/) and [First Contentful Paint (FCP)](https://web.dev/first-contentful-paint/). It also helps improve [Time to Interactive (TTI)](https://developer.chrome.com/en/docs/lighthouse/performance/interactive/), especially on slower devices. + +### Example + +`<Suspense>` works by wrapping a component that performs an asynchronous action (e.g. fetch data), showing fallback UI (e.g. skeleton, spinner) while it's happening, and then swapping in your component once the action completes. + +```tsx filename="app/dashboard/page.tsx" switcher +import { Suspense } from 'react' +import { PostFeed, Weather } from './Components' + +export default function Posts() { + return ( + <section> + <Suspense fallback={<p>Loading feed...</p>}> + <PostFeed /> + </Suspense> + <Suspense fallback={<p>Loading weather...</p>}> + <Weather /> + </Suspense> + </section> + ) +} +``` + +```jsx filename="app/dashboard/page.js" switcher +import { Suspense } from 'react' +import { PostFeed, Weather } from './Components' + +export default function Posts() { + return ( + <section> + <Suspense fallback={<p>Loading feed...</p>}> + <PostFeed /> + </Suspense> + <Suspense fallback={<p>Loading weather...</p>}> + <Weather /> + </Suspense> + </section> + ) +} +``` + +By using Suspense, you get the benefits of: + +1. **Streaming Server Rendering** - Progressively rendering HTML from the server to the client. +2. **Selective Hydration** - React prioritizes what components to make interactive first based on user interaction. + +For more Suspense examples and use cases, please see the [React Documentation](https://react.dev/reference/react/Suspense). + +### SEO + +- Next.js will wait for data fetching inside [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) to complete before streaming UI to the client. This guarantees the first part of a streamed response includes `<head>` tags. +- Since streaming is server-rendered, it does not impact SEO. You can use the [Mobile Friendly Test](https://search.google.com/test/mobile-friendly) tool from Google to see how your page appears to Google's web crawlers and view the serialized HTML ([source](https://web.dev/rendering-on-the-web/#seo-considerations)). diff --git a/docs/02-app/01-building-your-application/01-routing/07-error-handling.mdx b/docs/02-app/01-building-your-application/01-routing/07-error-handling.mdx new file mode 100644 index 0000000000000..3f47ae2c6e570 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/07-error-handling.mdx @@ -0,0 +1,215 @@ +--- +title: Error Handling +description: Handle runtime errors by automatically wrapping route segments and their nested children in a React Error Boundary. +--- + +The `error.js` file convention allows you to gracefully handle runtime errors in [nested routes](/docs/app/building-your-application/routing#nested-routes). + +- Automatically wrap a route segment and its nested children in a [React Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary). +- Create error UI tailored to specific segments using the file-system hierarchy to adjust granularity. +- Isolate errors to affected segments while keeping the rest of the app functional. +- Add functionality to attempt to recover from an error without a full page reload. + +Create error UI by adding an `error.js` file inside a route segment and exporting a React component: + +<Image + alt="error.js special file" + srcLight="/docs/light/error-special-file.png" + srcDark="/docs/dark/error-special-file.png" + width="1600" + height="606" +/> + +```tsx filename="app/dashboard/error.tsx" switcher +'use client' // Error components must be Client Components + +import { useEffect } from 'react' + +export default function Error({ + error, + reset, +}: { + error: Error + reset: () => void +}) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error) + }, [error]) + + return ( + <div> + <h2>Something went wrong!</h2> + <button + onClick={ + // Attempt to recover by trying to re-render the segment + () => reset() + } + > + Try again + </button> + </div> + ) +} +``` + +```jsx filename="app/dashboard/error.js" switcher +'use client' // Error components must be Client Components + +import { useEffect } from 'react' + +export default function Error({ error, reset }) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error) + }, [error]) + + return ( + <div> + <h2>Something went wrong!</h2> + <button + onClick={ + // Attempt to recover by trying to re-render the segment + () => reset() + } + > + Try again + </button> + </div> + ) +} +``` + +### How `error.js` Works + +<Image + alt="How error.js works" + srcLight="/docs/light/error-overview.png" + srcDark="/docs/dark/error-overview.png" + width="1600" + height="903" +/> + +- `error.js` automatically creates an [React Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) that **wraps** a nested child segment or `page.js` component. +- The React component exported from the `error.js` file is used as the **fallback** component. +- If an error is thrown within the error boundary, the error is **contained**, and the fallback component is **rendered**. +- When the fallback error component is active, layouts **above** the error boundary **maintain** their state and **remain** interactive, and the error component can display functionality to recover from the error. + +### Recovering From Errors + +The cause of an error can sometimes be temporary. In these cases, simply trying again might resolve the issue. + +An error component can use the `reset()` function to prompt the user to attempt to recover from the error. When executed, the function will try to re-render the Error boundary's contents. If successful, the fallback error component is replaced with the result of the re-render. + +```tsx filename="app/dashboard/error.tsx" switcher +'use client' + +export default function Error({ + error, + reset, +}: { + error: Error + reset: () => void +}) { + return ( + <div> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </div> + ) +} +``` + +```jsx filename="app/dashboard/error.js" switcher +'use client' + +export default function Error({ error, reset }) { + return ( + <div> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </div> + ) +} +``` + +### Nested Routes + +React components created through [special files](/docs/app/building-your-application/routing#file-conventions) are rendered in a [specific nested hierarchy](/docs/app/building-your-application/routing#component-hierarchy). + +For example, a nested route with two segments that both include `layout.js` and `error.js` files are rendered in the following _simplified_ component hierarchy: + +<Image + alt="Nested Error Component Hierarchy" + srcLight="/docs/light/nested-error-component-hierarchy.png" + srcDark="/docs/dark/nested-error-component-hierarchy.png" + width="1600" + height="687" +/> + +The nested component hierarchy has implications for the behavior of `error.js` files across a nested route: + +- Errors bubble up to the nearest parent error boundary. This means an `error.js` file will handle errors for all its nested child segments. More or less granular error UI can be achieved by placing `error.js` files at different levels in the nested folders of a route. +- An `error.js` boundary will **not** handle errors thrown in a `layout.js` component in the **same** segment because the error boundary is nested **inside** that layouts component. + +### Handling Errors in Layouts + +`error.js` boundaries do **not** catch errors thrown in `layout.js` or `template.js` components of the same segment. This [intentional hierarchy](#nested-routes) keeps important UI that is shared between sibling routes (such as navigation) visible and functional when an error occurs. + +To handle errors within a specific layout or template, place an `error.js` file in the layouts parent segment. + +To handle errors within the root layout or template, use a variation of `error.js` called `global-error.js`. + +### Handling Errors in Root Layouts + +The root `app/error.js` boundary does **not** catch errors thrown in the root `app/layout.js` or `app/template.js` component. + +To specifically handle errors in these root components, use a variation of `error.js` called `app/global-error.js` located in the root `app` directory. + +Unlike the root `error.js`, the `global-error.js` error boundary wraps the **entire** application, and its fallback component replaces the root layout when active. Because of this, it is important to note that `global-error.js` **must** define its own `<html>` and `<body>` tags. + +`global-error.js` is the least granular error UI and can be considered "catch-all" error handling for the whole application. It is unlikely to be triggered often as root components are typically less dynamic, and other `error.js` boundaries will catch most errors. + +Even if a `global-error.js` is defined, it is still recommended to define a root `error.js` whose fallback component will be rendered **within** the root layout, which includes globally shared UI and branding. + +```tsx filename="app/global-error.tsx" switcher +'use client' + +export default function GlobalError({ + error, + reset, +}: { + error: Error + reset: () => void +}) { + return ( + <html> + <body> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </body> + </html> + ) +} +``` + +```jsx filename="app/global-error.js" switcher +'use client' + +export default function GlobalError({ error, reset }) { + return ( + <html> + <body> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </body> + </html> + ) +} +``` + +### Handling Server Errors + +If an error is thrown during [data fetching](/docs/app/building-your-application/data-fetching/fetching) or inside a Server Component, Next.js will forward the resulting `Error` object to the nearest `error.js` file as the `error` prop. + +When running `next dev`, the `error` will be serialized and forwarded from the Server Component to the client `error.js`. To ensure security when running `next start` in production, a generic error message is forwarded to `error` along with a `.digest` which contains a hash of the error message. This hash can be used to correspond to server logs. diff --git a/docs/02-app/01-building-your-application/01-routing/08-parallel-routes.mdx b/docs/02-app/01-building-your-application/01-routing/08-parallel-routes.mdx new file mode 100644 index 0000000000000..a896c9516d6d7 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/08-parallel-routes.mdx @@ -0,0 +1,327 @@ +--- +title: Parallel Routes +description: Simultaneously render one or more pages in the same view that can be navigated independently. A pattern for highly dynamic applications. +--- + +Parallel Routing allows you to simultaneously or conditionally render one or more pages in the same layout. For highly dynamic sections of an app, such as dashboards and feeds on social sites, Parallel Routing can be used to implement complex routing patterns. + +For example, you can simultaneously render the team and analytics pages. + +<Image + alt="Parallel Routes Diagram" + srcLight="/docs/light/parallel-routes.png" + srcDark="/docs/dark/parallel-routes.png" + width="1600" + height="952" +/> + +Parallel Routing allows you to define independent error and loading states for each route as they're being streamed in independently. + +<Image + alt="Parallel routes enable custom error and loading states" + srcLight="/docs/light/parallel-routes-cinematic-universe.png" + srcDark="/docs/dark/parallel-routes-cinematic-universe.png" + width="1600" + height="1218" +/> + +Parallel Routing also allows you to conditionally render a slot based on certain conditions, such as authentication state. This enables fully separated code on the same URL. + +<Image + alt="Conditional routes diagram" + srcLight="/docs/light/conditional-routes-ui.png" + srcDark="/docs/dark/conditional-routes-ui.png" + width="1600" + height="898" +/> + +## Convention + +Parallel routes are created using named **slots**. Slots are defined with the `@folder` convention, and are passed to the same-level layout as props. + +> Slots are _not_ route segments and _do not affect the URL structure_. The file path `/@team/members` would be accessible at `/members`. + +For example, the following file structure defines two explicit slots: `@analytics` and `@team`. + +<Image + alt="Parallel Routes File-system Structure" + srcLight="/docs/light/parallel-routes-file-system.png" + srcDark="/docs/dark/parallel-routes-file-system.png" + width="1600" + height="687" +/> + +The folder structure above means that the component in `app/layout.js` now accepts the `@analytics` and `@team` slots props, and can render them in parallel alongside the `children` prop: + +```tsx filename="app/layout.tsx" switcher +export default function Layout(props: { + children: React.ReactNode + analytics: React.ReactNode + team: React.ReactNode +}) { + return ( + <> + {props.children} + {props.team} + {props.analytics} + </> + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default function Layout(props) { + return ( + <> + {props.children} + {props.team} + {props.analytics} + </> + ) +} +``` + +> **Good to know**: The `children` prop is an implicit slot that does not need to be mapped to a folder. This means `app/page.js` is equivalent to `app/@children/page.js`. + +## Unmatched Routes + +By default, the content rendered within a slot will match the current URL. + +In the case of an unmatched slot, the content that Next.js renders differs based on the routing technique and folder structure. + +### `default.js` + +You can define a `default.js` file to render as a fallback when Next.js cannot recover a slot's active state based on the current URL. + +Consider the following folder structure. The `@team` slot has a `settings` directory, but `@analytics` does not. + +<Image + alt="Parallel Routes unmatched routes" + srcLight="/docs/light/parallel-routes-unmatched-routes.png" + srcDark="/docs/dark/parallel-routes-unmatched-routes.png" + width="1600" + height="930" +/> + +If you were to navigate from the root `/` to `/settings`, the content that gets rendered is different based on the type of navigation and the availability of the `default.js` file. + +| | With `@analytics/default.js` | Without `@analytics/default.js` | +| --------------- | ---------------------------------------------------- | ------------------------------------------------- | +| Soft Navigation | `@team/settings/page.js` and `@analytics/page.js` | `@team/settings/page.js` and `@analytics/page.js` | +| Hard Navigation | `@team/settings/page.js` and `@analytics/default.js` | 404 | + +#### Soft Navigation + +On a [soft navigation](/docs/app/building-your-application/routing/linking-and-navigating#soft-navigation) - Next.js will render the slot's previously active state, even if it doesn't match the current URL. + +#### Hard Navigation + +On a [hard navigation](/docs/app/building-your-application/routing/linking-and-navigating#hard-navigation) - a navigation that requires a full page reload - Next.js will first try to render the unmatched slot's `default.js` file. If that's not available, a 404 gets rendered. + +> The 404 for unmatched routes helps ensure that you don't accidentally render a route that shouldn't be parallel rendered. + +## `useSelectedLayoutSegment(s)` + +Both [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment) and [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments) accept a `parallelRoutesKey`, which allows you read the active route segment within that slot. + +```tsx filename="app/layout.tsx" switcher +'use client' + +import { useSelectedLayoutSegment } from 'next/navigation' + +export default async function Layout(props: { + //... + authModal: React.ReactNode +}) { + const loginSegments = useSelectedLayoutSegment('authModal') + // ... +} +``` + +```jsx filename="app/layout.js" switcher +'use client' + +import { useSelectedLayoutSegment } from 'next/navigation' + +export default async function Layout(props) { + const loginSegments = useSelectedLayoutSegment('authModal') + // ... +} +``` + +When a user navigates to `@authModal/login`, or `/login` in the URL bar, `loginSegments` will be equal to the string `"login"`. + +## Examples + +### Modals + +Parallel Routing can be used to render modals. + +<Image + alt="Parallel Routes Diagram" + srcLight="/docs/light/parallel-routes-auth-modal.png" + srcDark="/docs/dark/parallel-routes-auth-modal.png" + width="1600" + height="687" +/> + +The `@authModal` slot renders a`<Modal>` component that can be shown by navigating to a matching route, for example `/login`. + +```tsx filename="app/layout.tsx" switcher +export default async function Layout(props: { + // ... + authModal: React.ReactNode +}) { + return ( + <> + {/* ... */} + {props.authModal} + </> + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default async function Layout(props) { + return ( + <> + {/* ... */} + {props.authModal} + </> + ) +} +``` + +```tsx filename="app/@authModal/login/page.tsx" switcher +import { Modal } from 'components/modal' + +export default function Login() { + return ( + <Modal> + <h1>Login</h1> + {/* ... */} + </Modal> + ) +} +``` + +```jsx filename="app/@authModal/login/page.js" switcher +import { Modal } from 'components/modal' + +export default function Login() { + return ( + <Modal> + <h1>Login</h1> + {/* ... */} + </Modal> + ) +} +``` + +To ensure that the contents of the modal don't get rendered when it's not active, you can create a `default.js` file that returns `null`. + +```tsx filename="app/@authModal/login/default.tsx" switcher +export default function Default() { + return null +} +``` + +```jsx filename="app/@authModal/login/default.js" switcher +export default function Default() { + return null +} +``` + +#### Dismissing a modal + +If a modal was initiated through client navigation, e.g. by using `<Link href="/login">`, you can dismiss the modal by calling `router.back()` or by using a `Link` component. + +```tsx filename="app/@authModal/login/page.tsx" highlight="5" switcher +'use client' +import { useRouter } from 'next/navigation' +import { Modal } from 'components/modal' + +export default async function Login() { + const router = useRouter() + return ( + <Modal> + <span onClick={() => router.back()}>Close modal</span> + <h1>Login</h1> + ... + </Modal> + ) +} +``` + +```jsx filename="app/@authModal/login/page.js" highlight="5" switcher +'use client' +import { useRouter } from 'next/navigation' +import { Modal } from 'components/modal' + +export default async function Login() { + const router = useRouter() + return ( + <Modal> + <span onClick={() => router.back()}>Close modal</span> + <h1>Login</h1> + ... + </Modal> + ) +} +``` + +> More information on modals is covered in the [Intercepting Routes](/docs/app/building-your-application/routing/intercepting-routes) section. + +If you want to navigate elsewhere and dismiss a modal, you can also use a catch-all route. + +<Image + alt="Parallel Routes Diagram" + srcLight="/docs/light/parallel-routes-catchall.png" + srcDark="/docs/dark/parallel-routes-catchall.png" + width="1600" + height="768" +/> + +```tsx filename="app/@authModal/[...catchAll]/page.tsx" switcher +export default function CatchAll() { + return null +} +``` + +```jsx filename="app/@authModal/[...catchAll]/page.js" switcher +export default function CatchAll() { + return null +} +``` + +> Catch-all routes take precedence over `default.js`. + +### Conditional Routes + +Parallel Routes can be used to implement conditional routing. For example, you can render a `@dashboard` or `@login` route depending on the authentication state. + +```tsx filename="app/layout.tsx" switcher +import { getUser } from '@/lib/auth' + +export default function Layout({ params, dashboard, login }) { + const isLoggedIn = getUser() + return isLoggedIn ? dashboard : login +} +``` + +```jsx filename="app/layout.js" switcher +import { getUser } from '@/lib/auth' + +export default function Layout({ params, dashboard, login }) { + const isLoggedIn = getUser() + return isLoggedIn ? dashboard : login +} +``` + +<Image + alt="Parallel routes authentication example" + srcLight="/docs/light/conditional-routes-ui.png" + srcDark="/docs/dark/conditional-routes-ui.png" + width="1600" + height="898" +/> diff --git a/docs/02-app/01-building-your-application/01-routing/09-intercepting-routes.mdx b/docs/02-app/01-building-your-application/01-routing/09-intercepting-routes.mdx new file mode 100644 index 0000000000000..9ae0950ce4f88 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/09-intercepting-routes.mdx @@ -0,0 +1,81 @@ +--- +title: Intercepting Routes +description: Use intercepting routes to load a new route within the current layout while masking the browser URL, useful for advanced routing patterns such as modals. +related: + title: Next Steps + description: Learn how to use modals with Intercepted and Parallel Routes. + links: + - app/building-your-application/routing/parallel-routes +--- + +Intercepting routes allows you to load a route within the current layout while keeping the context for the current page. This routing paradigm can be useful when you want to "intercept" a certain route to show a different route. + +For example, when clicking on a photo from within a feed, a modal overlaying the feed should show up with the photo. In this case, Next.js intercepts the `/feed` route and "masks" this URL to show `/photo/123` instead. + +<Image + alt="Intercepting routes soft navigation" + srcLight="/docs/light/intercepting-routes-soft-navigate.png" + srcDark="/docs/dark/intercepting-routes-soft-navigate.png" + width="1600" + height="617" +/> + +However, when navigating to the photo directly by for example when clicking a shareable URL or by refreshing the page, the entire photo page should render instead of the modal. No route interception should occur. + +<Image + alt="Intercepting routes hard navigation" + srcLight="/docs/light/intercepting-routes-hard-navigate.png" + srcDark="/docs/dark/intercepting-routes-hard-navigate.png" + width="1600" + height="604" +/> + +## Convention + +Intercepting routes can be defined with the `(..)` convention, which is similar to relative path convention `../` but for segments. + +You can use: + +- `(.)` to match segments on the **same level** +- `(..)` to match segments **one level above** +- `(..)(..)` to match segments **two levels above** +- `(...)` to match segments from the **root** `app` directory + +For example, you can intercept the `photo` segment from within the `feed` segment by creating a `(..)photo` directory. + +<Image + alt="Intercepting routes folder structure" + srcLight="/docs/light/intercepted-routes-files.png" + srcDark="/docs/dark/intercepted-routes-files.png" + width="1600" + height="604" +/> + +> Note that the `(..)` convention is based on _route segments_, not the file-system. + +## Examples + +### Modals + +Intercepting Routes can be used together with [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) to create modals. + +Using this pattern to create modals overcomes some common challenges when working with modals, by allowing you to: + +- Make the modal content **shareable through a URL** +- **Preserve context** when the page is refreshed, instead of closing the modal +- **Close the modal on backwards navigation** rather than going to the previous route +- **Reopen the modal on forwards navigation** + +<Image + alt="Intercepting routes modal example" + srcLight="/docs/light/intercepted-routes-modal-example.png" + srcDark="/docs/dark/intercepted-routes-modal-example.png" + width="1600" + height="976" +/> + +> In the above example, the path to the `photo` segment can use the `(..)` matcher since `@modal` is a _slot_ and not a _segment_. This means that the `photo` route is only one _segment_ level higher, despite being two _file-system_ levels higher. + +Other examples could include opening a login modal in a top navbar while also having a dedicated `/login` page, or opening a shopping cart in a side modal. + +[View an example](https://github.com/vercel-labs/nextgram) of modals with Intercepted and Parallel Routes. diff --git a/docs/02-app/01-building-your-application/01-routing/10-router-handlers.mdx b/docs/02-app/01-building-your-application/01-routing/10-router-handlers.mdx new file mode 100644 index 0000000000000..7b4b91560a1bf --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/10-router-handlers.mdx @@ -0,0 +1,589 @@ +--- +title: Route Handlers +description: Create custom request handlers for a given route using the Web's Request and Response APIs. +related: + title: API Reference + description: Learn more about the route.js file. + links: + - app/api-reference/file-conventions/route +--- + +Route Handlers allow you to create custom request handlers for a given route using the Web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) APIs. + +<Image + alt="Route.js Special File" + srcLight="/docs/light/route-special-file.png" + srcDark="/docs/dark/route-special-file.png" + width="1600" + height="444" +/> + +> **Good to know**: Route Handlers are only available inside the `app` directory. They are the equivalent of [API Routes](/docs/pages/building-your-application/routing/api-routes) inside the `pages` directory meaning you **do not** need to use API Routes and Route Handlers together. + +## Convention + +Route Handlers are defined in a [`route.js|ts` file](/docs/app/api-reference/file-conventions/route) inside the `app` directory: + +```ts filename="app/api/route.ts" switcher +export async function GET(request: Request) {} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) {} +``` + +Route Handlers can be nested inside the `app` directory, similar to `page.js` and `layout.js`. But there **cannot** be a `route.js` file at the same route segment level as `page.js`. + +### Supported HTTP Methods + +The following [HTTP methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS`. If an unsupported method is called, Next.js will return a `405 Method Not Allowed` response. + +### Extended `NextRequest` and `NextResponse` APIs + +In addition to supporting native [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). Next.js extends them with +[`NextRequest`](/docs/app/api-reference/functions/next-request) and [`NextResponse`](/docs/app/api-reference/functions/next-response) to provide convenient helpers for advanced use cases. + +## Behavior + +### Static Route Handlers + +Route Handlers are [statically evaluated](/docs/app/building-your-application/data-fetching#static-and-dynamic-data-fetching) by default when using the `GET` method with the `Response` object. + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const data = await res.json() + + return NextResponse.json({ data }) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const data = await res.json() + + return NextResponse.json({ data }) +} +``` + +> **TypeScript Warning:** Although `Response.json()` is valid, native TypeScript types currently shows an error, you can use [`NextResponse.json()`](/docs/app/api-reference/functions/next-response#json) for typed responses instead. + +### Dynamic Route Handlers + +Route handlers are evaluated dynamically when: + +- Using the `Request` object with the `GET` method. +- Using any of the other HTTP methods. +- Using [Dynamic Functions](/#dynamic-functions) like `cookies` and `headers`. +- The [Segment Config Options](#segment-config-options) manually specifies dynamic mode. + +For example: + +```ts filename="app/products/api/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url) + const id = searchParams.get('id') + const res = await fetch(`https://data.mongodb-api.com/product/${id}`, { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const product = await res.json() + + return NextResponse.json({ product }) +} +``` + +```js filename="app/products/api/route.js" switcher +import { NextResponse } from 'next/server' + +export async function GET(request) { + const { searchParams } = new URL(request.url) + const id = searchParams.get('id') + const res = await fetch(`https://data.mongodb-api.com/product/${id}`, { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const product = await res.json() + + return NextResponse.json({ product }) +} +``` + +Similarly, the `POST` method will cause the Route Handler to be evaluated dynamically. + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function POST() { + const res = await fetch('https://data.mongodb-api.com/...', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + body: JSON.stringify({ time: new Date().toISOString() }), + }) + + const data = await res.json() + + return NextResponse.json(data) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function POST() { + const res = await fetch('https://data.mongodb-api.com/...', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + body: JSON.stringify({ time: new Date().toISOString() }), + }) + + const data = await res.json() + + return NextResponse.json(data) +} +``` + +> **Good to know**: Previously, API Routes could have been used for use cases like handling form submissions. Route Handlers are likely not the solution for these uses cases. We will be recommending the use of [mutations](/docs/app/building-your-application/data-fetching/server-actions) for this when ready. + +### Route Resolution + +You can consider a `route` the lowest level routing primitive. + +- They **do not** participate in layouts or client-side navigations like `page`. +- There **cannot** be a `route.js` file at the same route as `page.js`. + +| Page | Route | Result | +| -------------------- | ------------------ | ---------------------------- | +| `app/page.js` | `app/route.js` | <Cross size={18} /> Conflict | +| `app/page.js` | `app/api/route.js` | <Check size={18} /> Valid | +| `app/[user]/page.js` | `app/api/route.js` | <Check size={18} /> Valid | + +Each `route.js` or `page.js` file takes over all HTTP verbs for that route. + +```jsx filename="app/page.js" +export default function Page() { + return <h1>Hello, Next.js!</h1> +} + +// ❌ Conflict +// `app/route.js` +export async function POST(request) {} +``` + +## Examples + +The following examples show how to combine Route Handlers with other Next.js APIs and features. + +### Revalidating Static Data + +You can [revalidate static data](/docs/app/building-your-application/data-fetching/revalidating) fetches using the [`next.revalidate`](/docs/app/building-your-application/data-fetching/fetching#revalidating-data) option: + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + next: { revalidate: 60 }, // Revalidate every 60 seconds + }) + const data = await res.json() + + return NextResponse.json(data) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + next: { revalidate: 60 }, // Revalidate every 60 seconds + }) + const data = await res.json() + + return NextResponse.json(data) +} +``` + +Alternatively, you can use the [`revalidate` segment config option](/docs/app/api-reference/file-conventions/route-segment-config#revalidate): + +```ts +export const revalidate = 60 +``` + +### Dynamic Functions + +Route Handlers can be used with dynamic functions from Next.js, like [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers). + +#### Cookies + +You can read cookies with [`cookies`](/docs/app/api-reference/functions/cookies) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function. + +This `cookies` instance is read-only. To set cookies, you need to return a new `Response` using the [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) header. + +```ts filename="app/api/route.ts" switcher +import { cookies } from 'next/headers' + +export async function GET(request: Request) { + const cookieStore = cookies() + const token = cookieStore.get('token') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { 'Set-Cookie': `token=${token}` }, + }) +} +``` + +```js filename="app/api/route.js" switcher +import { cookies } from 'next/headers' + +export async function GET(request) { + const cookieStore = cookies() + const token = cookieStore.get('token') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { 'Set-Cookie': `token=${token}` }, + }) +} +``` + +Alternatively, you can use abstractions on top of the underlying Web APIs to read cookies ([`NextRequest`](/docs/app/api-reference/functions/next-request)): + +```ts filename="app/api/route.ts" switcher +import { type NextRequest } from 'next/server' + +export async function GET(request: NextRequest) { + const token = request.cookies.get('token') +} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) { + const token = request.cookies.get('token') +} +``` + +#### Headers + +You can read headers with [`headers`](/docs/app/api-reference/functions/headers) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function. + +This `headers` instance is read-only. To set headers, you need to return a new `Response` with new `headers`. + +```ts filename="app/api/route.ts" switcher +import { headers } from 'next/headers' + +export async function GET(request: Request) { + const headersList = headers() + const referer = headersList.get('referer') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { referer: referer }, + }) +} +``` + +```js filename="app/api/route.js" switcher +import { headers } from 'next/headers' + +export async function GET(request) { + const headersList = headers() + const referer = headersList.get('referer') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { referer: referer }, + }) +} +``` + +Alternatively, you can use abstractions on top of the underlying Web APIs to read headers ([`NextRequest`](/docs/app/api-reference/functions/next-request)): + +```ts filename="app/api/route.ts" switcher +import { type NextRequest } from 'next/server' + +export async function GET(request: NextRequest) { + const requestHeaders = new Headers(request.headers) +} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) { + const requestHeaders = new Headers(request.headers) +} +``` + +### Redirects + +```ts filename="app/api/route.ts" switcher +import { redirect } from 'next/navigation' + +export async function GET(request: Request) { + redirect('https://nextjs.org/') +} +``` + +```js filename="app/api/route.js" switcher +import { redirect } from 'next/navigation' + +export async function GET(request) { + redirect('https://nextjs.org/') +} +``` + +### Dynamic Route Segments + +> We recommend reading the [Defining Routes](/docs/app/building-your-application/routing/defining-routes) page before continuing. + +Route Handlers can use [Dynamic Segments](/docs/app/building-your-application/routing/dynamic-routes) to create request handlers from dynamic data. + +```ts filename="app/items/[slug]/route.ts" switcher +export async function GET( + request: Request, + { params }: { params: { slug: string } } +) { + const slug = params.slug // 'a', 'b', or 'c' +} +``` + +```js filename="app/items/[slug]/route.js" switcher +export async function GET(request, { params }) { + const slug = params.slug // 'a', 'b', or 'c' +} +``` + +| Route | Example URL | `params` | +| --------------------------- | ----------- | --------------- | +| `app/items/[slug]/route.js` | `/items/a` | `{ slug: 'a' }` | +| `app/items/[slug]/route.js` | `/items/b` | `{ slug: 'b' }` | +| `app/items/[slug]/route.js` | `/items/c` | `{ slug: 'c' }` | + +### Streaming + +```ts filename="app/api/route.ts" switcher +// https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#convert_async_iterator_to_stream +function iteratorToStream(iterator: any) { + return new ReadableStream({ + async pull(controller) { + const { value, done } = await iterator.next() + + if (done) { + controller.close() + } else { + controller.enqueue(value) + } + }, + }) +} + +function sleep(time: number) { + return new Promise((resolve) => { + setTimeout(resolve, time) + }) +} + +const encoder = new TextEncoder() + +async function* makeIterator() { + yield encoder.encode('<p>One</p>') + await sleep(200) + yield encoder.encode('<p>Two</p>') + await sleep(200) + yield encoder.encode('<p>Three</p>') +} + +export async function GET() { + const iterator = makeIterator() + const stream = iteratorToStream(iterator) + + return new Response(stream) +} +``` + +```js filename="app/api/route.js" switcher +// https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#convert_async_iterator_to_stream +function iteratorToStream(iterator) { + return new ReadableStream({ + async pull(controller) { + const { value, done } = await iterator.next() + + if (done) { + controller.close() + } else { + controller.enqueue(value) + } + }, + }) +} + +function sleep(time) { + return new Promise((resolve) => { + setTimeout(resolve, time) + }) +} + +const encoder = new TextEncoder() + +async function* makeIterator() { + yield encoder.encode('<p>One</p>') + await sleep(200) + yield encoder.encode('<p>Two</p>') + await sleep(200) + yield encoder.encode('<p>Three</p>') +} + +export async function GET() { + const iterator = makeIterator() + const stream = iteratorToStream(iterator) + + return new Response(stream) +} +``` + +### Request Body + +You can read the `Request` body using the standard Web API methods: + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function POST(request: Request) { + const res = await request.json() + return NextResponse.json({ res }) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function POST(request) { + const res = await request.json() + return NextResponse.json({ res }) +} +``` + +### CORS + +You can set CORS headers on a `Response` using the standard Web API methods: + +```ts filename="app/api/route.ts" switcher +export async function GET(request: Request) { + return new Response('Hello, Next.js!', { + status: 200, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + }, + }) +} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) { + return new Response('Hello, Next.js!', { + status: 200, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + }, + }) +} +``` + +### Edge and Node.js Runtimes + +Route Handlers have an isomorphic Web API to support both Edge and Node.js runtimes seamlessly, including support for streaming. Since Route Handlers use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) as Pages and Layouts, they support long-awaited features like general-purpose [statically regenerated](/docs/app/building-your-application/data-fetching/revalidating) Route Handlers. + +You can use the `runtime` segment config option to specify the runtime: + +```ts +export const runtime = 'edge' // 'nodejs' is the default +``` + +### Non-UI Responses + +You can use Route Handlers to return non-UI content. Note that [`sitemap.xml`](/docs/app/api-reference/file-conventions/metadata/sitemap#generate-a-sitemap), [`robots.txt`](/docs/app/api-reference/file-conventions/metadata/robots#generate-a-robots-file), [`app icons`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx), and [open graph images](/docs/app/api-reference/file-conventions/metadata/opengraph-image) all have built-in support. + +```ts filename="app/rss.xml/route.ts" switcher +export async function GET() { + return new Response(`<?xml version="1.0" encoding="UTF-8" ?> +<rss version="2.0"> + +<channel> + <title>Next.js Documentation + https://nextjs.org/docs + The React Framework for the Web + + +`) +} +``` + +```js filename="app/rss.xml/route.js" switcher +export async function GET() { + return new Response(` + + + + Next.js Documentation + https://nextjs.org/docs + The React Framework for the Web + + +`) +} +``` + +### Segment Config Options + +Route Handlers use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) as pages and layouts. + +```ts filename="app/items/route.ts" switcher +export const dynamic = 'auto' +export const dynamicParams = true +export const revalidate = false +export const fetchCache = 'auto' +export const runtime = 'nodejs' +export const preferredRegion = 'auto' +``` + +```js filename="app/items/route.js" switcher +export const dynamic = 'auto' +export const dynamicParams = true +export const revalidate = false +export const fetchCache = 'auto' +export const runtime = 'nodejs' +export const preferredRegion = 'auto' +``` + +See the [API reference](/docs/app/api-reference/file-conventions/route-segment-config) for more details. diff --git a/docs/advanced-features/middleware.md b/docs/02-app/01-building-your-application/01-routing/11-middleware.mdx similarity index 68% rename from docs/advanced-features/middleware.md rename to docs/02-app/01-building-your-application/01-routing/11-middleware.mdx index 54842f3e95ea7..03edf864d0063 100644 --- a/docs/advanced-features/middleware.md +++ b/docs/02-app/01-building-your-application/01-routing/11-middleware.mdx @@ -1,49 +1,25 @@ --- +title: Middleware description: Learn how to use Middleware to run code before a request is completed. --- -# Middleware +Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly. -
-Version History +Middleware runs before cached content and routes are matched. See [Matching Paths](#matching-paths) for more details. -| Version | Changes | -| --------- | ------------------------------------------------------------------------------------------ | -| `v13.1.0` | Advanced Middleware flags added | -| `v13.0.0` | Middleware can modify request headers, response headers, and send responses | -| `v12.2.0` | Middleware is stable | -| `v12.0.9` | Enforce absolute URLs in Edge Runtime ([PR](https://github.com/vercel/next.js/pull/33410)) | -| `v12.0.0` | Middleware (Beta) added | +## Convention -
+Use the file `middleware.ts` (or `.js`) in the root of your project to define Middleware. For example, at the same level as `pages` or `app`, or inside `src` if applicable. -Middleware allows you to run code before a request is completed, then based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly. +## Example -Middleware runs _before_ cached content, so you can personalize static files and pages. Common examples of Middleware would be authentication, A/B testing, localized pages, bot protection, and more. Regarding localized pages, you can start with [i18n routing](/docs/advanced-features/i18n-routing) and implement Middleware for more advanced use cases. - -> **Note**: If you were using Middleware prior to `12.2`, please see the [upgrade guide](https://nextjs.org/docs/messages/middleware-upgrade-guide). - -## Using Middleware - -To begin using Middleware, follow the steps below: - -1. Install the latest version of Next.js: - -```bash -npm install next@latest -``` - -2. Create a `middleware.ts` (or `.js`) file at the same level as your `pages` (in the root or `src` directory) -3. Export a middleware function from the `middleware.ts` file: - -```typescript -// middleware.ts +```ts filename="middleware.ts" import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' // This function can be marked `async` if using `await` inside export function middleware(request: NextRequest) { - return NextResponse.redirect(new URL('/about-2', request.url)) + return NextResponse.redirect(new URL('/home', request.url)) } // See "Matching Paths" below to learn more @@ -60,21 +36,21 @@ Middleware will be invoked for **every route in your project**. The following is 2. `redirects` from `next.config.js` 3. Middleware (`rewrites`, `redirects`, etc.) 4. `beforeFiles` (`rewrites`) from `next.config.js` -5. Filesystem routes (`public/`, `_next/static/`, Pages, etc.) +5. Filesystem routes (`public/`, `_next/static/`, `pages/`, `app/`, etc.) 6. `afterFiles` (`rewrites`) from `next.config.js` 7. Dynamic Routes (`/blog/[slug]`) 8. `fallback` (`rewrites`) from `next.config.js` There are two ways to define which paths Middleware will run on: -1. Custom matcher config -2. Conditional statements +1. [Custom matcher config](#matcher) +2. [Conditional statements](#conditional-statements) ### Matcher `matcher` allows you to filter Middleware to run on specific paths. -```js +```js filename="middleware.js" export const config = { matcher: '/about/:path*', } @@ -82,7 +58,7 @@ export const config = { You can match a single path or multiple paths with an array syntax: -```js +```js filename="middleware.js" export const config = { matcher: ['/about/:path*', '/dashboard/:path*'], } @@ -90,7 +66,7 @@ export const config = { The `matcher` config allows full regex so matching like negative lookaheads or character matching is supported. An example of a negative lookahead to match all except specific paths can be seen here: -```js +```js filename="middleware.js" export const config = { matcher: [ /* @@ -105,7 +81,7 @@ export const config = { } ``` -> **Note**: The `matcher` values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored. +> **Good to know**: The `matcher` values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored. Configured matchers: @@ -116,13 +92,11 @@ Configured matchers: Read more details on [path-to-regexp](https://github.com/pillarjs/path-to-regexp#path-to-regexp-1) documentation. -> **Note**: For backward compatibility, Next.js always considers `/public` as `/public/index`. Therefore, a matcher of `/public/:path` will match. +> **Good to know**: For backward compatibility, Next.js always considers `/public` as `/public/index`. Therefore, a matcher of `/public/:path` will match. ### Conditional Statements -```typescript -// middleware.ts - +```ts filename="middleware.ts" import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' @@ -139,7 +113,7 @@ export function middleware(request: NextRequest) { ## NextResponse -The [`NextResponse`](#nextresponse) API allows you to: +The `NextResponse` API allows you to: - `redirect` the incoming request to a different URL - `rewrite` the response by displaying a given URL @@ -149,8 +123,8 @@ The [`NextResponse`](#nextresponse) API allows you to: To produce a response from Middleware, you can: -1. `rewrite` to a route ([Page](/docs/basic-features/pages.md) or [Edge API Route](/docs/api-routes/edge-api-routes.md)) that produces a response -2. return a `NextResponse` directly. See [Producing a Response](/docs/advanced-features/middleware#producing-a-response) +1. `rewrite` to a route ([Page](/docs/pages/building-your-application/routing/pages-and-layouts) or [Edge API Route](/docs/pages/building-your-application/routing/api-routes)) that produces a response +2. return a `NextResponse` directly. See [Producing a Response](#producing-a-response) ## Using Cookies @@ -159,16 +133,15 @@ Cookies are regular headers. On a `Request`, they are stored in the `Cookie` hea 1. For incoming requests, `cookies` comes with the following methods: `get`, `getAll`, `set`, and `delete` cookies. You can check for the existence of a cookie with `has` or remove all cookies with `clear`. 2. For outgoing responses, `cookies` have the following methods `get`, `getAll`, `set`, and `delete`. -```typescript -// middleware.ts +```ts filename="middleware.ts" import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware(request: NextRequest) { // Assume a "Cookie:nextjs=fast" header to be present on the incoming request // Getting cookies from the request using the `RequestCookies` API - let cookie = request.cookies.get('nextjs')?.value - console.log(cookie) // => 'fast' + let cookie = request.cookies.get('nextjs') + console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' } const allCookies = request.cookies.getAll() console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }] @@ -182,10 +155,10 @@ export function middleware(request: NextRequest) { response.cookies.set({ name: 'vercel', value: 'fast', - path: '/test', + path: '/', }) cookie = response.cookies.get('vercel') - console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/test' } + console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' } // The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header. return response @@ -196,9 +169,7 @@ export function middleware(request: NextRequest) { You can set request and response headers using the `NextResponse` API (setting _request_ headers is available since Next.js v13.0.0). -```ts -// middleware.ts - +```ts filename="middleware.ts" switcher import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' @@ -221,14 +192,35 @@ export function middleware(request: NextRequest) { } ``` -> **Note**: Avoid setting large headers as it might cause [431 Request Header Fields Too Large](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431) error depending on your backend web server configuration. +```js filename="middleware.js" switcher +import { NextResponse } from 'next/server' + +export function middleware(request) { + // Clone the request headers and set a new header `x-hello-from-middleware1` + const requestHeaders = new Headers(request.headers) + requestHeaders.set('x-hello-from-middleware1', 'hello') + + // You can also set request headers in NextResponse.rewrite + const response = NextResponse.next({ + request: { + // New request headers + headers: requestHeaders, + }, + }) + + // Set a new response header `x-hello-from-middleware2` + response.headers.set('x-hello-from-middleware2', 'hello') + return response +} +``` + +> **Good to know**: Avoid setting large headers as it might cause [431 Request Header Fields Too Large](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431) error depending on your backend web server configuration. ## Producing a Response You can respond from Middleware directly by returning a `Response` or `NextResponse` instance. (This is available since [Next.js v13.1.0](https://nextjs.org/blog/next-13-1#nextjs-advanced-middleware)) -```ts -// middleware.ts +```ts filename="middleware.ts" switcher import { NextRequest, NextResponse } from 'next/server' import { isAuthenticated } from '@lib/auth' @@ -249,22 +241,40 @@ export function middleware(request: NextRequest) { } ``` +```js filename="middleware.js" switcher +import { NextResponse } from 'next/server' +import { isAuthenticated } from '@lib/auth' + +// Limit the middleware to paths starting with `/api/` +export const config = { + matcher: '/api/:function*', +} + +export function middleware(request) { + // Call our authentication function to check the request + if (!isAuthenticated(request)) { + // Respond with JSON indicating an error message + return new NextResponse( + JSON.stringify({ success: false, message: 'authentication failed' }), + { status: 401, headers: { 'content-type': 'application/json' } } + ) + } +} +``` + ## Advanced Middleware Flags In `v13.1` of Next.js two additional flags were introduced for middleware, `skipMiddlewareUrlNormalize` and `skipTrailingSlashRedirect` to handle advanced use cases. `skipTrailingSlashRedirect` allows disabling Next.js default redirects for adding or removing trailing slashes allowing custom handling inside middleware which can allow maintaining the trailing slash for some paths but not others allowing easier incremental migrations. -```js -// next.config.js +```js filename="next.config.js" module.exports = { skipTrailingSlashRedirect: true, } ``` -```js -// middleware.js - +```js filename="middleware.js" const legacyPrefixes = ['/docs', '/blog'] export default async function middleware(req) { @@ -287,17 +297,13 @@ export default async function middleware(req) { `skipMiddlewareUrlNormalize` allows disabling the URL normalizing Next.js does to make handling direct visits and client-transitions the same. There are some advanced cases where you need full control using the original URL which this unlocks. -```js -// next.config.js - +```js filename="next.config.js" module.exports = { skipMiddlewareUrlNormalize: true, } ``` -```js -// middleware.js - +```js filename="middleware.js" export default async function middleware(req) { const { pathname } = req.nextUrl @@ -309,25 +315,12 @@ export default async function middleware(req) { } ``` -## Related - - - - - - +## Version History + +| Version | Changes | +| --------- | --------------------------------------------------------------------------------------------- | +| `v13.1.0` | Advanced Middleware flags added | +| `v13.0.0` | Middleware can modify request headers, response headers, and send responses | +| `v12.2.0` | Middleware is stable, please see the [upgrade guide](/docs/messages/middleware-upgrade-guide) | +| `v12.0.9` | Enforce absolute URLs in Edge Runtime ([PR](https://github.com/vercel/next.js/pull/33410)) | +| `v12.0.0` | Middleware (Beta) added | diff --git a/docs/02-app/01-building-your-application/01-routing/12-colocation.mdx b/docs/02-app/01-building-your-application/01-routing/12-colocation.mdx new file mode 100644 index 0000000000000..2585ce2c2a4c9 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/12-colocation.mdx @@ -0,0 +1,180 @@ +--- +title: Project Organization and File Colocation +nav_title: Project Organization +description: Learn how to organize your Next.js project and colocate files. +related: + links: + - app/building-your-application/routing/defining-routes + - app/building-your-application/routing/route-groups + - app/building-your-application/configuring/src-directory + - app/building-your-application/configuring/absolute-imports-and-module-aliases +--- + +Apart from [routing folder and file conventions](/docs/getting-started/project-structure#app-routing-conventions), Next.js is **unopinionated** about how you organize and colocate your project files. + +This page shares default behavior and features you can use to organize your project. + +- [Safe colocation by default](#safe-colocation-by-default) +- [Project organization features](#project-organization-features) +- [Project organization strategies](#project-organization-strategies) + +## Safe colocation by default + +In the `app` directory, [nested folder hierarchy](/docs/app/building-your-application/routing#route-segments) defines route structure. + +Each folder represents a route segment that is mapped to a corresponding segment in a URL path. + +However, even though route structure is defined through folders, a route is **not publically accessible** until a `page.js` or `route.js` file is added to a route segment. + +A diagram showing how a route is not publically accessible until a page.js or route.js file is added to a route segment. + +And, even when a route is made publically accessible, only the **content returned** by `page.js` or `route.js` is sent to the client. + +A diagram showing how page.js and route.js files make routes publically accessible. + +This means that **project files** can be **safely colocated** inside route segments in the `app` directory without accidentally being routable. + +A diagram showing colocated project files are not routable even when a segment contains a page.js or route.js file. + +> **Good to know**: +> +> - This is different from the `pages` directory, where any file in `pages` is considered a route. +> - While you **can** colocate your project files in `app` you don't **have** to. If you prefer, you can [keep them outside the `app` directory](#store-project-files-outside-of-app). + +## Project organization features + +Next.js provides several features to help you organize your project. + +### Private Folders + +Private folders can be created by prefixing a folder with an underscore: `_folderName` + +This indicates the folder is a private implementation detail and should not be considered by the routing system, thereby **opting the folder and all its subfolders** out of routing. + +An example folder structure using private folders + +Since files in the `app` directory can be [safely colocated by default](#safe-colocation-by-default), private folders are not required for colocation. However, they can be useful for: + +- Separating UI logic from routing logic. +- Consistently organizing internal files across a project and the Next.js ecosystem. +- Sorting and grouping files in code editors. +- Avoiding potential naming conflicts with future Next.js file conventions. + +> **Good to know** +> +> - While not a framework convention, you might also consider marking files outside private folders as "private" using the same underscore pattern. +> - You can create URL segments that start with an underscore by prefixing the folder name with `%5F` (the URL-encoded form of an underscore): `%5FfolderName`. +> - If you don't use private folders, it would be helpful to know Next.js [special file conventions](/docs/getting-started/project-structure#routing-files) to prevent unexpected naming conflicts. + +### Route Groups + +Route groups can be created by wrapping a folder in parenthesis: `(folderName)` + +This indicates the folder is for organizational purposes and should **not be included** in the route's URL path. + +An example folder structure using route groups + +Route groups are useful for: + +- [Organizing routes into groups](/docs/app/building-your-application/routing/route-groups#organize-routes-without-affecting-the-url-path) e.g. by site section, intent, or team. +- Enabling nested layouts in the same route segment level: + - [Creating multiple nested layouts in the same segment, including multiple root layouts](/docs/app/building-your-application/routing/route-groups#creating-multiple-root-layouts) + - [Adding a layout to a subset of routes in a common segment](/docs/app/building-your-application/routing/route-groups#opting-specific-segments-into-a-layout) + +### `src` Directory + +Next.js supports storing application code (including `app`) inside an optional [`src` directory](/docs/app/building-your-application/configuring/src-directory). This separates application code from project configuration files which mostly live in the root of a project. + +An example folder structure with the `src` directory + +### Module Path Aliases + +Next.js supports [Module Path Aliases](/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases) which make it easier to read and maintain imports across deeply nested project files. + +```jsx filename="app/dashboard/settings/analytics/page.js" +// before +import { Button } from '../../../components/button' + +// after +import { Button } from '@/components/button' +``` + +## Project organization strategies + +There is no "right" or "wrong" way when it comes to organizing your own files and folders in a Next.js project. + +The following section lists a very high-level overview of common strategies. The simplest takeaway is to choose a strategy that works for you and your team and be consistent across the project. + +> **Good to know**: In our examples below, we're using `components` and `lib` folders as generalized placeholders, their naming has no special framework significance and your projects might use other folders like `ui`, `utils`, `hooks`, `styles`, etc. + +### Store project files outside of `app` + +This strategy stores all application code in shared folders in the **root of your project** and keeps the `app` directory purely for routing purposes. + +An example folder structure with project files outside of app + +### Store project files in top-level folders inside of `app` + +This strategy stores all application code in shared folders in the **root of the `app` directory**. + +An example folder structure with project files inside app + +### Split project files by feature or route + +This strategy stores globally shared application code in the root `app` directory and **splits** more specific application code into the route segments that use them. + +An example folder structure with project files split by feature or route diff --git a/docs/02-app/01-building-your-application/01-routing/13-internationalization.mdx b/docs/02-app/01-building-your-application/01-routing/13-internationalization.mdx new file mode 100644 index 0000000000000..fc6286ef1299f --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/13-internationalization.mdx @@ -0,0 +1,153 @@ +--- +title: Internationalization +description: Add support for multiple languages with internationalized routing and localized content. +--- + +Next.js enables you to configure the routing and rendering of content to support multiple languages. Making your site adaptive to different locales includes translated content (localization) and internationalized routes. + +## Terminology + +- **Locale:** An identifier for a set of language and formatting preferences. This usually includes the preferred language of the user and possibly their geographic region. + - `en-US`: English as spoken in the United States + - `nl-NL`: Dutch as spoken in the Netherlands + - `nl`: Dutch, no specific region + +## Routing Overview + +It’s recommended to use the user’s language preferences in the browser to select which locale to use. Changing your preferred language will modify the incoming `Accept-Language` header to your application. + +For example, using the following libraries, you can look at an incoming `Request` to determine which locale to select, based on the `Headers`, locales you plan to support, and the default locale. + +```js filename="middleware.js" +import { match } from '@formatjs/intl-localematcher' +import Negotiator from 'negotiator' + +let headers = { 'accept-language': 'en-US,en;q=0.5' } +let languages = new Negotiator({ headers }).languages() +let locales = ['en-US', 'nl-NL', 'nl'] +let defaultLocale = 'en-US' + +match(languages, locales, defaultLocale) // -> 'en-US' +``` + +Routing can be internationalized by either the sub-path (`/fr/products`) or domain (`my-site.fr/products`). With this information, you can now redirect the user based on the locale inside [Middleware](/docs/app/building-your-application/routing/middleware). + +```js filename="middleware.js" +import { NextResponse } from 'next/server' + +let locales = ['en-US', 'nl-NL', 'nl'] + +// Get the preferred locale, similar to above or using a library +function getLocale(request) { ... } + +export function middleware(request) { + // Check if there is any supported locale in the pathname + const pathname = request.nextUrl.pathname + const pathnameIsMissingLocale = locales.every( + (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}` + ) + + // Redirect if there is no locale + if (pathnameIsMissingLocale) { + const locale = getLocale(request) + + // e.g. incoming request is /products + // The new URL is now /en-US/products + return NextResponse.redirect( + new URL(`/${locale}/${pathname}`, request.url) + ) + } +} + +export const config = { + matcher: [ + // Skip all internal paths (_next) + '/((?!_next).*)', + // Optional: only run on root (/) URL + // '/' + ], +} +``` + +Finally, ensure all special files inside `app/` are nested under `app/[lang]`. This enables the Next.js router to dynamically handle different locales in the route, and forward the `lang` parameter to every layout and page. For example: + +```jsx filename="app/[lang]/page.js" +// You now have access to the current locale +// e.g. /en-US/products -> `lang` is "en-US" +export default async function Page({ params: { lang } }) { + return ... +} +``` + +The root layout can also be nested in the new folder (e.g. `app/[lang]/layout.js`). + +## Localization + +Changing displayed content based on the user’s preferred locale, or localization, is not something specific to Next.js. The patterns described below would work the same with any web application. + +Let’s assume we want to support both English and Dutch content inside our application. We might maintain two different “dictionaries”, which are objects that give us a mapping from some key to a localized string. For example: + +```json filename="dictionaries/en.json" +{ + "products": { + "cart": "Add to Cart" + } +} +``` + +```json filename="dictionaries/nl.json" +{ + "products": { + "cart": "Toevoegen aan Winkelwagen" + } +} +``` + +We can then create a `getDictionary` function to load the translations for the requested locale: + +```jsx filename="app/[lang]/dictionaries.js" +import 'server-only' + +const dictionaries = { + en: () => import('./dictionaries/en.json').then((module) => module.default), + nl: () => import('./dictionaries/nl.json').then((module) => module.default), +} + +export const getDictionary = async (locale) => dictionaries[locale]() +``` + +Given the currently selected language, we can fetch the dictionary inside of a layout or page. + +```jsx filename="app/[lang]/page.js" +import { getDictionary } from './dictionaries' + +export default async function Page({ params: { lang } }) { + const dict = await getDictionary(lang) // en + return // Add to Cart +} +``` + +Because all layouts and pages in the `app/` directory default to [Server Components](/docs/getting-started/react-essentials#server-components), we do not need to worry about the size of the translation files affecting our client-side JavaScript bundle size. This code will **only run on the server**, and only the resulting HTML will be sent to the browser. + +## Static Generation + +To generate static routes for a given set of locales, we can use `generateStaticParams` with any page or layout. This can be global, for example, in the root layout: + +```jsx filename="app/[lang]/layout.js" +export async function generateStaticParams() { + return [{ lang: 'en-US' }, { lang: 'de' }] +} + +export default function Root({ children, params }) { + return ( + + {children} + + ) +} +``` + +## Examples + +- [Minimal i18n routing and translations](https://github.com/vercel/next.js/tree/canary/examples/app-dir-i18n-routing) +- [next-intl](https://next-intl-docs.vercel.app/docs/next-13) diff --git a/docs/02-app/01-building-your-application/01-routing/index.mdx b/docs/02-app/01-building-your-application/01-routing/index.mdx new file mode 100644 index 0000000000000..8859c0ccbc6bb --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/index.mdx @@ -0,0 +1,184 @@ +--- +title: Routing Fundamentals +nav_title: Routing +description: Learn the fundamentals of routing for front-end applications. +--- + +The skeleton of every application is routing. This page will introduce you to the **fundamental concepts** of routing for the web and how to handle routing in Next.js. + +## Terminology + +First, you will see these terms being used throughout the documentation. Here's a quick reference: + +Terminology for Component Tree + +- **Tree:** A convention for visualizing a hierarchical structure. For example, a component tree with parent and children components, a folder structure, etc. +- **Subtree:** Part of a tree, starting at a new root (first) and ending at the leaves (last). +- **Root**: The first node in a tree or subtree, such as a root layout. +- **Leaf:** Nodes in a subtree that have no children, such as the last segment in a URL path. + +Terminology for URL Anatomy + +- **URL Segment:** Part of the URL path delimited by slashes. +- **URL Path:** Part of the URL that comes after the domain (composed of segments). + +## The `app` Router + +In version 13, Next.js introduced a new **App Router** built on [React Server Components](/docs/getting-started/react-essentials#server-components), which supports shared layouts, nested routing, loading states, error handling, and more. + +The App Router works in a new directory named `app`. The `app` directory works alongside the `pages` directory to allow for incremental adoption. This allows you to opt some routes of your application into the new behavior while keeping other routes in the `pages` directory for previous behavior. If your application uses the `pages` directory, please also see the [Pages Router](/docs/pages/building-your-application/routing) documentation. + +> **Good to know**: The App Router takes priority over the Pages Router. Routes across directories should not resolve to the same URL path and will cause a build-time error to prevent a conflict. + +Next.js App Directory + +By default, components inside `app` are [React Server Components](/docs/getting-started/react-essentials#server-components). This is a performance optimization and allows you to easily adopt them, and you can also use [Client Components](/docs/getting-started/react-essentials#client-components). + +> **Recommendation:** Check out the [Server and Client Components](/docs/getting-started/react-essentials) page if you're new to Server Components. + +## Roles of Folders and Files + +Next.js uses a file-system based router where: + +- **Folders** are used to define routes. A route is a single path of nested folders, following the file-system hierarchy from the **root folder** down to a final **leaf folder** that includes a `page.js` file. See [Defining Routes](/docs/app/building-your-application/routing/defining-routes). +- **Files** are used to create UI that is shown for a route segment. See [special files](#file-conventions). + +## Route Segments + +Each folder in a route represents a **route segment**. Each route segment is mapped to a corresponding **segment** in a **URL path**. + +How Route Segments Map to URL Segments + +## Nested Routes + +To create a nested route, you can nest folders inside each other. For example, you can add a new `/dashboard/settings` route by nesting two new folders in the `app` directory. + +The `/dashboard/settings` route is composed of three segments: + +- `/` (Root segment) +- `dashboard` (Segment) +- `settings` (Leaf segment) + +## File Conventions + +Next.js provides a set of special files to create UI with specific behavior in nested routes: + +| | | +| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| [`layout`](/docs/app/building-your-application/routing/pages-and-layouts#layouts) | Shared UI for a segment and its children | +| [`page`](/docs/app/building-your-application/routing/pages-and-layouts#pages) | Unique UI of a route and make routes publicly accessible | +| [`loading`](/docs/app/building-your-application/routing/loading-ui-and-streaming) | Loading UI for a segment and its children | +| [`not-found`](/docs/app/api-reference/file-conventions/not-found) | Not found UI for a segment and its children | +| [`error`](/docs/app/building-your-application/routing/error-handling) | Error UI for a segment and its children | +| [`global-error`](/docs/app/building-your-application/routing/error-handling) | Global Error UI | +| [`route`](/docs/app/building-your-application/routing/router-handlers) | Server-side API endpoint | +| [`template`](/docs/app/building-your-application/routing/pages-and-layouts#templates) | Specialized re-rendered Layout UI | +| [`default`](/docs/app/api-reference/file-conventions/default) | Fallback UI for [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) | + +> **Good to know**: `.js`, `.jsx`, or `.tsx` file extensions can be used for special files. + +## Component Hierarchy + +The React components defined in special files of a route segment are rendered in a specific hierarchy: + +- `layout.js` +- `template.js` +- `error.js` (React error boundary) +- `loading.js` (React suspense boundary) +- `not-found.js` (React error boundary) +- `page.js` or nested `layout.js` + +Component Hierarchy for File Conventions + +In a nested route, the components of a segment will be nested **inside** the components of its parent segment. + +Nested File Conventions Component Hierarchy + +## Colocation + +In addition to special files, you have the option to colocate your own files (e.g. components, styles, tests etc) inside folders in the `app` directory. + +This is because while folders define routes, only the contents returned by `page.js` or `route.js` are publically addressable. + +An example folder structure with colocated files + +Learn more about [Project Organization and Colocation](/docs/app/building-your-application/routing/colocation). + +## Server-Centric Routing with Client-side Navigation + +Unlike the `pages` directory which uses client-side routing, the App Router uses **server-centric routing** to align with [Server Components](/docs/getting-started/react-essentials#server-components) and [data fetching on the server](/docs/app/building-your-application/data-fetching/fetching). With server-centric routing, the client does not have to download a route map and the same request for Server Components can be used to look up routes. This optimization is useful for all applications, but has a larger impact on applications with many routes. + +Although routing is server-centric, the router uses **client-side navigation** with the [Link Component](/docs/app/building-your-application/routing/linking-and-navigating#link-component) - resembling the behavior of a Single-Page Application. This means when a user navigates to a new route, the browser will not reload the page. Instead, the URL will be updated and Next.js will [only render the segments that change](#partial-rendering). + +Additionally, as users navigate around the app, the router will store the result of the React Server Component payload in an **in-memory client-side cache**. The cache is split by route segments which allows invalidation at any level and ensures consistency across [React's concurrent renders](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react). This means that for certain cases, the cache of a previously fetched segment can be re-used, further improving performance. + +Learn more about [Linking and Navigating](/docs/app/building-your-application/routing/linking-and-navigating). + +## Partial Rendering + +When navigating between sibling routes (e.g. `/dashboard/settings` and `/dashboard/analytics` below), Next.js will only fetch and render the layouts and pages in routes that change. It will **not** re-fetch or re-render anything above the segments in the subtree. This means that in routes that share a layout, the layout will be preserved when a user navigates between sibling pages. + +How partial rendering works + +Without partial rendering, each navigation would cause the full page to re-render on the server. Rendering only the segment that’s updating reduces the amount of data transferred and execution time, leading to improved performance. + +## Advanced Routing Patterns + +The App Router also provides a set conventions to help you implement more advanced routing patterns. These include: + +- [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes): Allow you to simultaneously show two or more pages in the same view that can be navigated independently. You can use them for split views that have their own sub-navigation. E.g. Dashboards. +- [Intercepting Routes](/docs/app/building-your-application/routing/intercepting-routes): Allow you to intercept a route and show it in the context of another route. You can use these when keeping the context for the current page is important. E.g. Seeing all tasks while editing one task or expanding a photo in a feed. + +These patterns allow you to build richer and more complex UIs, democratizing features that were historically complex for small teams and individual developers to implement. + +## Next Steps + +Now that you understand the fundamentals of routing in Next.js, follow the links below to create your first routes: diff --git a/docs/02-app/01-building-your-application/02-rendering/01-static-and-dynamic-rendering.mdx b/docs/02-app/01-building-your-application/02-rendering/01-static-and-dynamic-rendering.mdx new file mode 100644 index 0000000000000..aca039d8b42f7 --- /dev/null +++ b/docs/02-app/01-building-your-application/02-rendering/01-static-and-dynamic-rendering.mdx @@ -0,0 +1,56 @@ +--- +title: Static and Dynamic Rendering +nav_title: Static and Dynamic +description: Learn about static and dynamic rendering in Next.js. +--- + +In Next.js, a route can be statically or dynamically rendered. + +- In a **static** route, components are rendered on the server at build time. The result of the work is cached and reused on subsequent requests. +- In a **dynamic** route, components are rendered on the server at request time. + +## Static Rendering (Default) + +By default, Next.js statically renders routes to improve performance. This means all the rendering work is done ahead of time and can be served from a Content Delivery Network (CDN) geographically closer to the user. + +## Static Data Fetching (Default) + +By default, Next.js will cache the result of `fetch()` requests that do not specifically opt out of caching behavior. This means that fetch requests that do not set a `cache` option will use the `force-cache` option. + +If any fetch requests in the route use the `revalidate` option, the route will be re-rendered statically during revalidation. + +To learn more about caching data fetching requests, see the [Caching and Revalidating](/docs/app/building-your-application/data-fetching/caching) page. + +## Dynamic Rendering + +During static rendering, if a dynamic function or a dynamic `fetch()` request (no caching) is discovered, Next.js will switch to dynamically rendering the whole route at request time. Any cached data requests can still be re-used during dynamic rendering. + +This table summarizes how [dynamic functions](#dynamic-functions) and [caching](#static-data-fetching-default) affect the rendering behavior of a route: + +| Data Fetching | Dynamic Functions | Rendering | +| --------------- | ----------------- | --------- | +| Static (Cached) | No | Static | +| Static (Cached) | Yes | Dynamic | +| Not Cached | No | Dynamic | +| Not Cached | Yes | Dynamic | + +Note how dynamic functions always opt the route into dynamic rendering, regardless of whether the data fetching is cached or not. In other words, static rendering is dependent not only on the data fetching behavior, but also on the dynamic functions used in the route. + +> **Good to know**: In the future, Next.js will introduce hybrid server-side rendering where layouts and pages in a route can be independently statically or dynamically rendered, instead of the whole route. + +### Dynamic Functions + +Dynamic functions rely on information that can only be known at request time such as a user's cookies, current requests headers, or the URL's search params. In Next.js, these dynamic functions are: + +- Using [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers) in a Server Component will opt the whole route into dynamic rendering at request time. +- Using [`useSearchParams()`](/docs/app/api-reference/functions/use-params) in Client Components will skip static rendering and instead render all Client Components up to the nearest parent Suspense boundary on the client. + - We recommend wrapping the Client Component that uses `useSearchParams()` in a `` boundary. This will allow any Client Components above it to be statically rendered. [Example](/docs/app/api-reference/functions/use-search-params#static-rendering). +- Using the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) [Pages](/docs/app/api-reference/file-conventions/page) prop will opt the page into dynamic rendering at request time. + +## Dynamic Data Fetching + +Dynamic data fetches are `fetch()` requests that specifically opt out of caching behavior by setting the `cache` option to `'no-store'` or `revalidate` to `0`. + +The caching options for all `fetch` requests in a layout or page can also be set using the [segment config](/docs/app/api-reference/file-conventions/route-segment-config) object. + +To learn more about Dynamic Data Fetching, see the [Data Fetching](/docs/app/building-your-application/data-fetching/fetching) page. diff --git a/docs/02-app/01-building-your-application/02-rendering/02-edge-and-nodejs-runtimes.mdx b/docs/02-app/01-building-your-application/02-rendering/02-edge-and-nodejs-runtimes.mdx new file mode 100644 index 0000000000000..55b798b6693bd --- /dev/null +++ b/docs/02-app/01-building-your-application/02-rendering/02-edge-and-nodejs-runtimes.mdx @@ -0,0 +1,71 @@ +--- +title: Edge and Node.js Runtimes +description: Learn about the switchable runtimes (Edge and Node.js) in Next.js. +--- + +In the context of Next.js, "runtime" refers to the set of libraries, APIs, and general functionality available to your code during execution. + +Next.js has two server runtimes where you can render parts of your application code: + +- [Node.js Runtime](#nodejs-runtime) +- [Edge Runtime](#edge-runtime) + +Each runtime has its own set of APIs. Please refer to the [Node.js Docs](https://nodejs.org/docs/latest/api/) and [Edge Docs](/docs/app/api-reference/edge) for the full list of available APIs. Both runtimes can also support [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) depending on your deployment infrastructure. + +By default, the `app` directory uses the Node.js runtime. However, you can opt into different runtimes (e.g. Edge) on a per-route basis. + +## Runtime Differences + +There are many considerations to make when choosing a runtime. This table shows the major differences at a glance. If you want a more in-depth analysis of the differences, check out the sections below. + +| | Node | Serverless | Edge | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ---------------- | +| [Cold Boot](https://vercel.com/docs/concepts/get-started/compute#cold-and-hot-boots?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) | / | ~250ms | Instant | +| [HTTP Streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) | Yes | Yes | Yes | +| IO | All | All | `fetch` | +| Scalability | / | High | Highest | +| Security | Normal | High | High | +| Latency | Normal | Low | Lowest | +| npm Packages | All | All | A smaller subset | + +### Edge Runtime + +In Next.js, the lightweight Edge Runtime is a subset of available Node.js APIs. + +The Edge Runtime is ideal if you need to deliver dynamic, personalized content at low latency with small, simple functions. The Edge Runtime's speed comes from its minimal use of resources, but that can be limiting in many scenarios. + +For example, code executed in the Edge Runtime [on Vercel cannot exceed between 1 MB and 4 MB](https://vercel.com/docs/concepts/limits/overview#edge-middleware-and-edge-functions-size), this limit includes imported packages, fonts and files, and will vary depending on your deployment infrastructure. + +### Node.js Runtime + +Using the Node.js runtime gives you access to all Node.js APIs, and all npm packages that rely on them. However, it's not as fast to start up as routes using the Edge runtime. + +Deploying your Next.js application to a Node.js server will require managing, scaling, and configuring your infrastructure. Alternatively, you can consider deploying your Next.js application to a serverless platform like Vercel, which will handle this for you. + +### Serverless Node.js + +Serverless is ideal if you need a scalable solution that can handle more complex computational loads than the Edge Runtime. With Serverless Functions on Vercel, for example, your overall code size is [50MB](https://vercel.com/docs/concepts/limits/overview#serverless-function-size) including imported packages, fonts, and files. + +The downside compared to routes using the [Edge](https://vercel.com/docs/concepts/functions/edge-functions) is that it can take hundreds of milliseconds for Serverless Functions to boot up before they begin processing requests. Depending on the amount of traffic your site receives, this could be a frequent occurrence as the functions are not frequently "warm". + + + +## Examples + +### Segment Runtime Option + +You can specify a runtime for individual route segments in your Next.js application. To do so, [declare a variable called `runtime` and export it](/docs/app/api-reference/file-conventions/route-segment-config). The variable must be a string, and must have a value of either `'nodejs'` or `'edge'` runtime. + +The following example demonstrates a page route segment that exports a `runtime` with a value of `'edge'`: + +```tsx filename="app/page.tsx" switcher +export const runtime = 'edge' // 'nodejs' (default) | 'edge' +``` + +```jsx filename="app/page.js" switcher +export const runtime = 'edge' // 'nodejs' (default) | 'edge' +``` + +If the segment runtime is _not_ set, the default `nodejs` runtime will be used. You do not need to use the `runtime` option if you do not plan to change from the Node.js runtime. + + diff --git a/docs/02-app/01-building-your-application/02-rendering/index.mdx b/docs/02-app/01-building-your-application/02-rendering/index.mdx new file mode 100644 index 0000000000000..5d1d1dcd71214 --- /dev/null +++ b/docs/02-app/01-building-your-application/02-rendering/index.mdx @@ -0,0 +1,73 @@ +--- +title: Rendering +description: Learn the differences between Next.js rendering environments, strategies, and runtimes. +--- + +Rendering converts the code you write into user interfaces. + +React 18 and Next.js 13 introduced new ways to render your application. This page will help you understand the differences between rendering environments, strategies, runtimes, and how to opt into them. + +## Rendering Environments + +There are two environments where your application code can be rendered: the client and the server. + +Client and Server Environemnts + +- The **client** refers to the browser on a user's device that sends a request to a server for your application code. It then turns the response from the server into an interface the user can interact with. +- The **server** refers to the computer in a data center that stores your application code, receives requests from a client, does some computation, and sends back an appropriate response. + +> **Good to know**: Server can refer to computers in regions where your application is deployed to, the [Edge Network](https://vercel.com/docs/concepts/edge-network/overview) where your application code is distributed, or [Content Delivery Networks (CDNs)](https://developer.mozilla.org/en-US/docs/Glossary/CDN) where the result of the rendering work can be cached. + +## Component-level Client and Server Rendering + +Before React 18, the primary way to render your application **using React** was entirely on the client. + +Next.js provided an easier way to break down your application into **pages** and prerender on the server by generating HTML and sending it to the client to be [hydrated](https://react.dev/reference/react-dom/hydrate#hydrating-server-rendered-html) by React. However, this led to additional JavaScript needed on the client to make the initial HTML interactive. + +Now, with [Server and Client Components](/docs/getting-started/react-essentials), React can render on the client **and** the server meaning you can choose the rendering environment at the component level. + +By default, the [`app` router uses **Server Components**](/docs/getting-started/react-essentials#server-components), allowing you to easily render components on the server and reducing the amount of JavaScript sent to the client. + +## Static and Dynamic Rendering on the Server + +In addition to client-side and server-side rendering with React components, Next.js gives you the option to optimize rendering on the server with **Static** and **Dynamic** Rendering. + +### Static Rendering + +With **Static Rendering**, both Server _and_ Client Components can be prerendered on the server at **build time**. The result of the work is [cached](/docs/app/building-your-application/data-fetching/caching) and reused on subsequent requests. The cached result can also be [revalidated](/docs/app/building-your-application/data-fetching#revalidating-data). + +> **Good to know**: This is equivalent to [Static Site Generation (SSG)](/docs/pages/building-your-application/rendering/static-site-generation) and [Incremental Static Regeneration (ISR)](/docs/pages/building-your-application/rendering/incremental-static-regeneration) in the [Pages Router](/docs/pages/building-your-application/rendering). + +Server and Client Components are rendered differently during Static Rendering: + +- Client Components have their HTML and JSON prerendered and cached on the server. The cached result is then sent to the client for hydration. +- Server Components are rendered on the server by React, and their payload is used to generate HTML. The same rendered payload is also used to hydrate the components on the client, resulting in no JavaScript needed on the client. + +### Dynamic Rendering + +With Dynamic Rendering, both Server _and_ Client Components are rendered on the server at **request time**. The result of the work is not cached. + +> **Good to know**: This is equivalent to [Server-Side Rendering (`getServerSideProps()`)](/docs/pages/building-your-application/rendering/server-side-rendering) in the [Pages Router](/docs/pages/building-your-application/rendering). + +To learn more about static and dynamic behavior, see the [Static and Dynamic Rendering](/docs/app/building-your-application/rendering/static-and-dynamic-rendering) page. To learn more about caching, see the [Caching](/docs/app/building-your-application/data-fetching/caching) sections. + +## Edge and Node.js Runtimes + +On the server, there are two runtimes where your pages can be rendered: + +- The **Node.js Runtime** (default) has access to all Node.js APIs and compatible packages from the ecosystem. +- The **Edge Runtime** is based on [Web APIs](/docs/app/api-reference/edge). + +Both runtimes support [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) from the server, depending on your deployment infrastructure. + +To learn how to switch between runtimes, see the [Edge and Node.js Runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) page. + +## Next Steps + +Now that you understand the fundamentals of rendering, you can learn more about implementing the different rendering strategies and runtimes: diff --git a/docs/02-app/01-building-your-application/03-data-fetching/01-fetching.mdx b/docs/02-app/01-building-your-application/03-data-fetching/01-fetching.mdx new file mode 100644 index 0000000000000..aa282804f3e7e --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/01-fetching.mdx @@ -0,0 +1,401 @@ +--- +title: Data Fetching +nav_title: Fetching +description: Learn how to fetch data in your Next.js application. +--- + +The Next.js App Router allows you to fetch data directly in your React components by marking the function as `async` and using `await` for the [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + +Data fetching is built on top of the [`fetch()` Web API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and React Server Components. When using `fetch()`, requests are [automatically deduped](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) by default. + +Next.js extends the `fetch` options object to allow each request to set its own [caching and revalidating](/docs/app/building-your-application/data-fetching/caching). + +## `async` and `await` in Server Components + +You can use `async` and `await` to fetch data in Server Components. + +```tsx filename="app/page.tsx" switcher +async function getData() { + const res = await fetch('https://api.example.com/...') + // The return value is *not* serialized + // You can return Date, Map, Set, etc. + + // Recommendation: handle errors + if (!res.ok) { + // This will activate the closest `error.js` Error Boundary + throw new Error('Failed to fetch data') + } + + return res.json() +} + +export default async function Page() { + const data = await getData() + + return
+} +``` + +```jsx filename="app/page.js" switcher +async function getData() { + const res = await fetch('https://api.example.com/...') + // The return value is *not* serialized + // You can return Date, Map, Set, etc. + + // Recommendation: handle errors + if (!res.ok) { + // This will activate the closest `error.js` Error Boundary + throw new Error('Failed to fetch data') + } + + return res.json() +} + +export default async function Page() { + const data = await getData() + + return
+} +``` + +> **Good to know**: +> +> To use an `async` Server Component with TypeScript, ensure you are using TypeScript `5.1.3` or higher and `@types/react` `18.2.8` or higher. + +### Server Component Functions + +Next.js provides helpful server functions you may need when fetching data in Server Components: + +- [`cookies()`](/docs/app/api-reference/functions/cookies) +- [`headers()`](/docs/app/api-reference/functions/headers) + +## `use` in Client Components + +`use` is a new React function that **accepts a promise** conceptually similar to `await`. `use` **handles the promise** returned by a function in a way that is compatible with components, hooks, and Suspense. Learn more about `use` in the [React RFC](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). + +Wrapping `fetch` in `use` is currently **not** recommended in Client Components and may trigger multiple re-renders. For now, if you need to fetch data in a Client Component, we recommend using a third-party library such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/v4). + +> **Good to know**: We'll be adding more examples once `fetch` and `use` work in Client Components. + +## Static Data Fetching + +By default, `fetch` will automatically fetch and [cache data](/docs/app/building-your-application/data-fetching/caching) indefinitely. + +```ts +fetch('https://...') // cache: 'force-cache' is the default +``` + +### Revalidating Data + +To revalidate [cached data](/docs/app/building-your-application/data-fetching/caching) at a timed interval, you can use the `next.revalidate` option in `fetch()` to set the `cache` lifetime of a resource (in seconds). + +```ts +fetch('https://...', { next: { revalidate: 10 } }) +``` + +See [Revalidating Data](/docs/app/building-your-application/data-fetching/revalidating) for more information. + +> **Good to know**: +> +> Caching at the fetch level with `revalidate` or `cache: 'force-cache'` stores the data across requests in a shared cache. You should avoid using it for user-specific data (i.e. requests that derive data from `cookies()` or `headers()`) + +## Dynamic Data Fetching + +To fetch fresh data on every `fetch` request, use the `cache: 'no-store'` option. + +```ts +fetch('https://...', { cache: 'no-store' }) +``` + +## Data Fetching Patterns + +### Parallel Data Fetching + +To minimize client-server waterfalls, we recommend this pattern to fetch data in parallel: + +```tsx filename="app/artist/[username]/page.tsx" switcher +import Albums from './albums' + +async function getArtist(username: string) { + const res = await fetch(`https://api.example.com/artist/${username}`) + return res.json() +} + +async function getArtistAlbums(username: string) { + const res = await fetch(`https://api.example.com/artist/${username}/albums`) + return res.json() +} + +export default async function Page({ + params: { username }, +}: { + params: { username: string } +}) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumsData = getArtistAlbums(username) + + // Wait for the promises to resolve + const [artist, albums] = await Promise.all([artistData, albumsData]) + + return ( + <> +

{artist.name}

+ + + ) +} +``` + +```jsx filename="app/artist/[username]/page.js" switcher +import Albums from './albums' + +async function getArtist(username) { + const res = await fetch(`https://api.example.com/artist/${username}`) + return res.json() +} + +async function getArtistAlbums(username) { + const res = await fetch(`https://api.example.com/artist/${username}/albums`) + return res.json() +} + +export default async function Page({ params: { username } }) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumsData = getArtistAlbums(username) + + // Wait for the promises to resolve + const [artist, albums] = await Promise.all([artistData, albumsData]) + + return ( + <> +

{artist.name}

+ + + ) +} +``` + +By starting the fetch prior to calling `await` in the Server Component, each request can eagerly start to fetch requests at the same time. This sets the components up so you can avoid waterfalls. + +We can save time by initiating both requests in parallel, however, the user won't see the rendered result until both promises are resolved. + +To improve the user experience, you can add a [suspense boundary](/docs/app/building-your-application/routing/loading-ui-and-streaming) to break up the rendering work and show part of the result as soon as possible: + +```tsx filename="artist/[username]/page.tsx" switcher +import { getArtist, getArtistAlbums, type Album } from './api' + +export default async function Page({ + params: { username }, +}: { + params: { username: string } +}) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumData = getArtistAlbums(username) + + // Wait for the artist's promise to resolve first + const artist = await artistData + + return ( + <> +

{artist.name}

+ {/* Send the artist information first, + and wrap albums in a suspense boundary */} + Loading...}> + + + + ) +} + +// Albums Component +async function Albums({ promise }: { promise: Promise }) { + // Wait for the albums promise to resolve + const albums = await promise + + return ( +
    + {albums.map((album) => ( +
  • {album.name}
  • + ))} +
+ ) +} +``` + +```jsx filename="artist/[username]/page.js" switcher +import { getArtist, getArtistAlbums } from './api' + +export default async function Page({ params: { username } }) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumData = getArtistAlbums(username) + + // Wait for the artist's promise to resolve first + const artist = await artistData + + return ( + <> +

{artist.name}

+ {/* Send the artist information first, + and wrap albums in a suspense boundary */} + Loading...}> + + + + ) +} + +// Albums Component +async function Albums({ promise }) { + // Wait for the albums promise to resolve + const albums = await promise + + return ( +
    + {albums.map((album) => ( +
  • {album.name}
  • + ))} +
+ ) +} +``` + +Take a look at the [preloading pattern](/docs/app/building-your-application/data-fetching/caching#preload-pattern-with-cache) for more information on improving components structure. + +### Sequential Data Fetching + +To fetch data sequentially, you can `fetch` directly inside the component that needs it, or you can `await` the result of `fetch` inside the component that needs it: + +```tsx filename="app/artist/page.tsx" switcher +// ... + +async function Playlists({ artistID }: { artistID: string }) { + // Wait for the playlists + const playlists = await getArtistPlaylists(artistID) + + return ( +
    + {playlists.map((playlist) => ( +
  • {playlist.name}
  • + ))} +
+ ) +} + +export default async function Page({ + params: { username }, +}: { + params: { username: string } +}) { + // Wait for the artist + const artist = await getArtist(username) + + return ( + <> +

{artist.name}

+ Loading...}> + + + + ) +} +``` + +```jsx filename="app/artist/page.js" switcher +// ... + +async function Playlists({ artistID }) { + // Wait for the playlists + const playlists = await getArtistPlaylists(artistID) + + return ( +
    + {playlists.map((playlist) => ( +
  • {playlist.name}
  • + ))} +
+ ) +} + +export default async function Page({ params: { username } }) { + // Wait for the artist + const artist = await getArtist(username) + + return ( + <> +

{artist.name}

+ Loading...}> + + + + ) +} +``` + +By fetching data inside the component, each fetch request and nested segment in the route cannot start fetching data and rendering until the previous request or segment has completed. + +### Blocking Rendering in a Route + +By fetching data in a [layout](/docs/app/building-your-application/routing/pages-and-layouts), rendering for all route segments beneath it can only start once the data has finished loading. + +In the `pages` directory, pages using server-rendering would show the browser loading spinner until `getServerSideProps` had finished, then render the React component for that page. This can be described as "all or nothing" data fetching. Either you had the entire data for your page, or none. + +In the `app` directory, you have additional options to explore: + +1. First, you can use `loading.js` to show an instant loading state from the server while streaming in the result from your data fetching function. +2. Second, you can move data fetching _lower_ in the component tree to only block rendering for the parts of the page that need it. For example, moving data fetching to a specific component rather than fetching it at the root layout. + +Whenever possible, it's best to fetch data in the segment that uses it. This also allows you to show a loading state for only the part of the page that is loading, and not the entire page. + +## Data Fetching without `fetch()` + +You might not always have the ability to use and configure `fetch` requests directly if you're using a third-party library such as an ORM or database client. + +In cases where you cannot use `fetch` but still want to control the caching or revalidating behavior of a layout or page, you can rely on the [default caching behavior](#default-caching-behavior) of the segment or use the [segment cache configuration](#segment-cache-configuration). + +### Default Caching Behavior + +Any data fetching libraries that do not use `fetch` directly **will not** affect caching of a route, and will be static or dynamic depending on the route segment. + +If the segment is static (default), the output of the request will be cached and revalidated (if configured) alongside the rest of the segment. If the segment is dynamic, the output of the request will _not_ be cached and will be re-fetched on every request when the segment is rendered. + +> **Good to know**: Dynamic functions like [`cookies()`](/docs/app/api-reference/functions/cookies) and [`headers()`](/docs/app/api-reference/functions/headers) will make the route segment dynamic. + +### Segment Cache Configuration + +As a temporary solution, until the caching behavior of third-party queries can be configured, you can use [segment configuration](/docs/app/api-reference/file-conventions/route-segment-config#revalidate) to customize the cache behavior of the entire segment. + +```tsx filename="app/page.tsx" switcher +import prisma from './lib/prisma' + +export const revalidate = 3600 // revalidate every hour + +async function getPosts() { + const posts = await prisma.post.findMany() + return posts +} + +export default async function Page() { + const posts = await getPosts() + // ... +} +``` + +```jsx filename="app/page.js" switcher +import prisma from './lib/prisma' + +export const revalidate = 3600 // revalidate every hour + +async function getPosts() { + const posts = await prisma.post.findMany() + return posts +} + +export default async function Page() { + const posts = await getPosts() + // ... +} +``` diff --git a/docs/02-app/01-building-your-application/03-data-fetching/02-caching.mdx b/docs/02-app/01-building-your-application/03-data-fetching/02-caching.mdx new file mode 100644 index 0000000000000..a5b9517baa95e --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/02-caching.mdx @@ -0,0 +1,284 @@ +--- +title: Caching Data +nav_title: Caching +description: Learn about caching routes in Next.js. +--- + +Next.js has built-in support for caching data, both on a per-request basis (recommended) or for an entire route segment. + +Fetch Request Deduplication + +## Per-request Caching + +### `fetch()` + +By default, all `fetch()` requests are cached and deduplicated automatically. This means that if you make the same request twice, the second request will reuse the result from the first request. + +```tsx filename="app/page.tsx" switcher +async function getComments() { + const res = await fetch('https://...') // The result is cached + return res.json() +} + +// This function is called twice, but the result is only fetched once +const comments = await getComments() // cache MISS + +// The second call could be anywhere in your application +const comments = await getComments() // cache HIT +``` + +```jsx filename="app/page.js" switcher +async function getComments() { + const res = await fetch('https://...') // The result is cached + return res.json() +} + +// This function is called twice, but the result is only fetched once +const comments = await getComments() // cache MISS + +// The second call could be anywhere in your application +const comments = await getComments() // cache HIT +``` + +Requests are **not** cached if: + +- Dynamic methods (`next/headers`, `export const POST`, or similar) are used and the fetch is a `POST` request (or uses `Authorization` or `cookie` headers) +- `fetchCache` is configured to skip cache by default +- `revalidate: 0` or `cache: 'no-store'` is configured on individual `fetch` + +Requests made using `fetch` can specify a `revalidate` option to control the revalidation frequency of the request. + +```tsx filename="app/page.tsx" switcher +export default async function Page() { + // revalidate this data every 10 seconds at most + const res = await fetch('https://...', { next: { revalidate: 10 } }) + const data = res.json() + // ... +} +``` + +```jsx filename="app/page.js" switcher +export default async function Page() { + // revalidate this data every 10 seconds at most + const res = await fetch('https://...', { next: { revalidate: 10 } }) + const data = res.json() + // ... +} +``` + +### React `cache()` + +React allows you to [`cache()`](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md) and deduplicate requests, memoizing the result of the wrapped function call. The same function called with the same arguments will reuse a cached value instead of re-running the function. + +```ts filename="utils/getUser.ts" switcher +import { cache } from 'react' + +export const getUser = cache(async (id: string) => { + const user = await db.user.findUnique({ id }) + return user +}) +``` + +```js filename="utils/getUser.js" switcher +import { cache } from 'react' + +export const getUser = cache(async (id) => { + const user = await db.user.findUnique({ id }) + return user +}) +``` + +```tsx filename="app/user/[id]/layout.tsx" switcher +import { getUser } from '@utils/getUser' + +export default async function UserLayout({ params: { id } }) { + const user = await getUser(id) + // ... +} +``` + +```jsx filename="app/user/[id]/layout.js" switcher +import { getUser } from '@utils/getUser' + +export default async function UserLayout({ params: { id } }) { + const user = await getUser(id) + // ... +} +``` + +```tsx filename="app/user/[id]/page.tsx" switcher +import { getUser } from '@utils/getUser' + +export default async function Page({ + params: { id }, +}: { + params: { id: string } +}) { + const user = await getUser(id) + // ... +} +``` + +```jsx filename="app/user/[id]/page.js" switcher +import { getUser } from '@utils/getUser' + +export default async function Page({ params: { id } }) { + const user = await getUser(id) + // ... +} +``` + +Although the `getUser()` function is called twice in the example above, only one query will be made to the database. This is because `getUser()` is wrapped in `cache()`, so the second request can reuse the result from the first request. + +> **Good to know**: +> +> - `fetch()` caches requests automatically, so you don't need to wrap functions that use `fetch()` with `cache()`. See [automatic request deduping](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) for more information. +> - In this new model, we recommend **fetching data directly in the component that needs it**, even if you're requesting the same data in multiple components, rather than passing the data between components as props. +> - We recommend using the [`server-only` package](/docs/getting-started/react-essentials#keeping-server-only-code-out-of-client-components-poisoning) to make sure server data fetching functions are never used on the client. + +### `POST` requests and `cache()` + +`POST` requests are automatically deduplicated when using `fetch` – unless they are inside of `POST` Route Handler or come after reading `headers()`/`cookies()`. For example, if you are using GraphQL and `POST` requests in the above cases, you can use `cache` to deduplicate requests. The `cache` arguments must be flat and only include primitives. Deep objects won't match for deduplication. + +```ts filename="utils/getUser.ts" switcher +import { cache } from 'react' + +export const getUser = cache(async (id: string) => { + const res = await fetch('...', { method: 'POST', body: '...' }) + // ... +}) +``` + +```js filename="utils/getUser.js" switcher +import { cache } from 'react' + +export const getUser = cache(async (id) => { + const res = await fetch('...', { method: 'POST', body: '...' }) + // ... +}) +``` + +### Preload pattern with `cache()` + +As a pattern, we suggest optionally exposing a `preload()` export in utilities or components that do data fetching. + +```tsx filename="components/User.tsx" switcher +import { getUser } from '@utils/getUser' + +export const preload = (id: string) => { + // void evaluates the given expression and returns undefined + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void + void getUser(id) +} +export default async function User({ id }: { id: string }) { + const result = await getUser(id) + // ... +} +``` + +```jsx filename="components/User.js" switcher +import { getUser } from '@utils/getUser' + +export const preload = (id) => { + // void evaluates the given expression and returns undefined + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void + void getUser(id) +} +export default async function User({ id }) { + const result = await getUser(id) + // ... +} +``` + +By calling `preload`, you can eagerly start fetching data you're likely going to need. + +```tsx filename="app/user/[id]/page.tsx" switcher +import User, { preload } from '@components/User' + +export default async function Page({ + params: { id }, +}: { + params: { id: string } +}) { + preload(id) // starting loading the user data now + const condition = await fetchCondition() + return condition ? : null +} +``` + +```jsx filename="app/user/[id]/page.js" switcher +import User, { preload } from '@components/User' + +export default async function Page({ params: { id } }) { + preload(id) // starting loading the user data now + const condition = await fetchCondition() + return condition ? : null +} +``` + +> **Good to know**: +> +> - The `preload()` function can have any name. It's a pattern, not an API. +> - This pattern is completely optional and something you can use to optimize on a case-by-case basis. +> This pattern is a further optimization on top of [parallel data fetching](/docs/app/building-your-application/data-fetching/fetching#parallel-data-fetching). Now you don't have to pass promises down as props and can instead rely on the preload pattern. + +### Combining `cache`, `preload`, and `server-only` + +You can combine the `cache` function, the `preload` pattern, and the `server-only` package to create a data fetching utility that can be used throughout your app. + +```ts filename="utils/getUser.ts" switcher +import { cache } from 'react' +import 'server-only' + +export const preload = (id: string) => { + void getUser(id) +} + +export const getUser = cache(async (id: string) => { + // ... +}) +``` + +```js filename="utils/getUser.js" switcher +import { cache } from 'react' +import 'server-only' + +export const preload = (id) => { + void getUser(id) +} + +export const getUser = cache(async (id) => { + // ... +}) +``` + +With this approach, you can eagerly fetch data, cache responses, and guarantee that this data fetching [only happens on the server](/docs/getting-started/react-essentials#keeping-server-only-code-out-of-client-components-poisoning). + +The `getUser.ts` exports can be used by layouts, pages, or components to give them control over when a user's data is fetched. + +## Segment-level Caching + +> **Good to know**: We recommend using per-request caching for improved granularity and control over caching. + +Segment-level caching allows you to cache and revalidate data used in route segments. + +This mechanism allows different segments of a path to control the cache lifetime of the entire route. Each `page.tsx` and `layout.tsx` in the route hierarchy can export a `revalidate` value that sets the revalidation time for the route. + +```tsx filename="app/page.tsx" switcher +export const revalidate = 60 // revalidate this segment every 60 seconds +``` + +```jsx filename="app/page.js" switcher +export const revalidate = 60 // revalidate this segment every 60 seconds +``` + +> **Good to know**: +> +> - If a page, layout, and fetch request inside components all specify a [`revalidate`](/docs/app/api-reference/file-conventions/route-segment-config#revalidate) frequency, the lowest value of the three will be used. +> - Advanced: You can set `fetchCache` to `'only-cache'` or `'force-cache'` to ensure that all `fetch` requests opt into caching but the revalidation frequency might still be lowered by individual `fetch` requests. See [`fetchCache`](/docs/app/api-reference/file-conventions/route-segment-config) for more information. diff --git a/docs/02-app/01-building-your-application/03-data-fetching/03-revalidating.mdx b/docs/02-app/01-building-your-application/03-data-fetching/03-revalidating.mdx new file mode 100644 index 0000000000000..f2c2a0ca38fc0 --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/03-revalidating.mdx @@ -0,0 +1,103 @@ +--- +title: Revalidating Data +nav_title: Revalidating +description: Learn about revalidating data in Next.js using Incremental Static Regeneration. +--- + +Next.js allows you to update specific static routes **without needing to rebuild your entire site**. Revalidation (also known as [Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration)) allows you to retain the benefits of static while scaling to millions of pages. + +There are two types of revalidation in Next.js: + +- **Background**: Revalidates the data at a specific time interval. +- **On-demand**: Revalidates the data based on an event such as an update. + +## Background Revalidation + +To revalidate cached data at a specific interval, you can use the `next.revalidate` option in `fetch()` to set the `cache` lifetime of a resource (in seconds). + +```js +fetch('https://...', { next: { revalidate: 60 } }) +``` + +If you want to revalidate data that does not use `fetch` (i.e. using an external package or query builder), you can use the [route segment config](/docs/app/api-reference/file-conventions/route-segment-config#revalidate). + +```tsx filename="app/page.tsx" switcher +export const revalidate = 60 // revalidate this page every 60 seconds +``` + +```jsx filename="app/page.js" switcher +export const revalidate = 60 // revalidate this page every 60 seconds +``` + +In addition to `fetch`, you can also revalidate data using [`cache`](/docs/app/building-your-application/data-fetching/caching#per-request-caching). + +### How it works + +1. When a request is made to the route that was statically rendered at build time, it will initially show the cached data. +2. Any requests to the route after the initial request and before 60 seconds are also cached and instantaneous. +3. After the 60-second window, the next request will still show the cached (stale) data. +4. Next.js will trigger a regeneration of the data in the background. +5. Once the route generates successfully, Next.js will invalidate the cache and show the updated route. If the background regeneration fails, the old data would still be unaltered. + +When a request is made to a route segment that hasn’t been generated, Next.js will dynamically render the route on the first request. Future requests will serve the static route segments from the cache. + +> **Good to know**: Check if your upstream data provider has caching enabled by default. You might need to disable (e.g. `useCdn: false`), otherwise a revalidation won't be able to pull fresh data to update the ISR cache. Caching can occur at a CDN (for an endpoint being requested) when it returns the `Cache-Control` header. ISR on Vercel [persists the cache globally and handles rollbacks](https://vercel.com/docs/concepts/incremental-static-regeneration/overview). + +## On-demand Revalidation + +If you set a `revalidate` time of `60`, all visitors will see the same generated version of your site for one minute. The only way to invalidate the cache is if someone visits the page after the minute has passed. + +The Next.js App Router supports revalidating content on-demand based on a route or cache tag. This allows you to manually purge the Next.js cache for specific fetches, making it easier to update your site when: + +- Content from your headless CMS is created or updated. +- Ecommerce metadata changes (price, description, category, reviews, etc). + +### Using On-Demand Revalidation + +Data can be revalidated on-demand by path ([`revalidatePath`](/docs/app/api-reference/functions/revalidatePath)) or by cache tag ([`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)). + +For example, the following `fetch` adds the cache tag `collection`: + +```tsx filename="app/page.tsx" switcher +export default async function Page() { + const res = await fetch('https://...', { next: { tags: ['collection'] } }) + const data = await res.json() + // ... +} +``` + +```jsx filename="app/page.js" switcher +export default async function Page() { + const res = await fetch('https://...', { next: { tags: ['collection'] } }) + const data = await res.json() + // ... +} +``` + +This cached data can then be revalidated on-demand by calling `revalidateTag` in a [Route Handler](/docs/app/building-your-application/routing/router-handlers). + +```ts filename="app/api/revalidate/route.ts" switcher +import { NextRequest, NextResponse } from 'next/server' +import { revalidateTag } from 'next/cache' + +export async function GET(request: NextRequest) { + const tag = request.nextUrl.searchParams.get('tag') + revalidateTag(tag) + return NextResponse.json({ revalidated: true, now: Date.now() }) +} +``` + +```js filename="app/api/revalidate/route.js" switcher +import { NextResponse } from 'next/server' +import { revalidateTag } from 'next/cache' + +export async function GET(request) { + const tag = request.nextUrl.searchParams.get('tag') + revalidateTag(tag) + return NextResponse.json({ revalidated: true, now: Date.now() }) +} +``` + +## Error Handling and Revalidation + +If an error is thrown while attempting to revalidate data, the last successfully generated data will continue to be served from the cache. On the next subsequent request, Next.js will retry revalidating the data. diff --git a/docs/02-app/01-building-your-application/03-data-fetching/04-server-actions.mdx b/docs/02-app/01-building-your-application/03-data-fetching/04-server-actions.mdx new file mode 100644 index 0000000000000..89e02b1c79b32 --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/04-server-actions.mdx @@ -0,0 +1,562 @@ +--- +title: Server Actions +nav_title: Server Actions +description: Use Server Actions to mutate data in your Next.js application. +related: + title: Next Steps + description: For more information on what to do next, we recommend the following sections + links: + - app/api-reference/functions/cookies +--- + +[Server Actions](#server-actions) are an **alpha** feature in Next.js, built on top of React [Actions](#actions). They enable server-side data mutations, reduced client-side JavaScript, and progressively enhanced forms. They can be defined inside Server Components and/or called from Client Components: + +**With Server Components:** + +```jsx filename="app/add-to-cart.js" highlight={5, 6, 13} +import { cookies } from 'next/headers' + +// Server action defined inside a Server Component +export default function AddToCart({ productId }) { + async function addItem(data) { + 'use server' + + const cartId = cookies().get('cartId')?.value + await saveToDb({ cartId, data }) + } + + return ( +
+ +
+ ) +} +``` + +**With Client Components:** + +```jsx filename="app/actions.js" highlight={1, 3} +'use server' + +async function addItem(data) { + const cartId = cookies().get('cartId')?.value + await saveToDb({ cartId, data }) +} +``` + +```jsx filename="app/add-to-cart.js" highlight={1, 3, 8} +'use client' + +import { addItem } from './actions.js' + +// Server Action being called inside a Client Component +export default function AddToCart({ productId }) { + return ( +
+ +
+ ) +} +``` + +> **Good to know:** +> +> - Using Server Actions will opt into running the React `experimental` channel. +> - React Actions, `useOptimistic`, and `useFormStatus` are not a Next.js or React Server Components specific features. +> - Next.js integrates React Actions into the Next.js router, bundler, and caching system, including adding data mutation APIs like `revalidateTag` and `revalidatePath`. + +## Convention + +You can enable Server Actions in your Next.js project by enabling the **experimental** `serverActions` flag. + +```js filename="next.config.js" +module.exports = { + experimental: { + serverActions: true, + }, +} +``` + +### Creation + +Server Actions can be defined in two places: + +- Inside the component that uses it (Server Components only) +- In a separate file (Client and Server Components), for reusability. You can define multiple Server Actions in a single file. + +#### With Server Components + +Create a Server Action by defining an asynchronous function with the `"use server"` directive at the top of the function body. This function should have **serializable arguments** and a **serializable return value** based on the React Server Components protocol. + +```jsx filename="app/server-component.js" highlight={2} +export default function ServerComponent() { + async function myAction() { + 'use server' + // ... + } +} +``` + +#### With Client Components + +If you're using a Server Action inside a Client Component, create your action in a separate file with the "use server" directive at the top of the file. Then, import the Server Action into your Client Component: + +```js filename="app/actions.js" highlight={1} +'use server' + +export async function myAction() { + // ... +} +``` + +```jsx filename="app/client-component.js" highlight={1} +'use client' + +import { myAction } from './actions' + +export default function ClientComponent() { + return ( +
+ +
+ ) +} +``` + +> **Good to know**: When using a top-level `"use server"` directive, all exports below will be considered Server Actions. You can have multiple Server Actions in a single file. + +### Invocation + +You can invoke Server Actions using the following methods: + +- Using `action`: React's `action` prop allows invoking a Server Action on a `
` element. +- Using `formAction`: React's `formAction` prop allows handling ` +
+ ) +} +``` + +> **Good to know**: An `action` is similar to the HTML primitive [`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action) + +#### `formAction` + +You can use `formAction` prop to handle **Form Actions** on elements such as `button`, `input type="submit"`, and `input type="image"`. The `formAction` prop takes precedence over the form's `action`. + +```jsx filename="app/form.js" highlight={15} +export default function Form() { + async function handleSubmit() { + 'use server' + // ... + } + + async function submitImage() { + 'use server' + // ... + } + + return ( +
+ + + +
+ ) +} +``` + +> **Good to know**: A `formAction` is the HTML primitive [`formaction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#formaction). React now allows you to pass functions to this attribute. + +#### Custom invocation using `startTransition` + +You can also invoke Server Actions without using `action` or `formAction`. You can achieve this by using `startTransition` provided by the `useTransition` hook, which can be useful if you want to use Server Actions outside of forms, buttons, or inputs. + +> **Good to know**: Using `startTransition` disables the out-of-the-box Progressive Enhancement. + +```jsx filename="app/components/example-client-component.js" highlight={3, 7, 10} +'use client' + +import { useTransition } from 'react' +import { addItem } from '../actions' + +function ExampleClientComponent({ id }) { + let [isPending, startTransition] = useTransition() + + return ( + + ) +} +``` + +```js filename="app/actions.js" +'use server' + +export async function addItem(id) { + await addItemToDb(id) + // Marks all product pages for revalidating + revalidatePath('/product/[id]') +} +``` + +#### Custom invocation without `startTransition` + +If you aren't doing [Server Mutations](#server-mutations), you can directly pass the function as a prop like any other function. + +```tsx filename="app/posts/[id]/page.tsx" switcher +import kv from '@vercel/kv' +import LikeButton from './like-button' + +export default function Page({ params }: { params: { id: string } }) { + async function increment() { + 'use server' + await kv.incr(`post:id:${params.id}`) + } + + return +} +``` + +```jsx filename="app/posts/[id]/page.js" switcher +import kv from '@vercel/kv' +import LikeButton from './like-button' + +export default function Page({ params }) { + async function increment() { + 'use server' + await kv.incr(`post:id:${params.id}`) + } + + return +} +``` + +```tsx filename="app/post/[id]/like-button.tsx" switcher +'use client' + +export default function LikeButton({ + increment, +}: { + increment: () => Promise +}) { + return ( + + ) +} +``` + +```jsx filename="app/post/[id]/like-button.js" switcher +'use client' + +export default function LikeButton({ increment }) { + return ( + + ) +} +``` + +### Enhancements + +#### Experimental `useOptimistic` + +The experimental `useOptimistic` hook provides a way to implement optimistic updates in your application. Optimistic updates are a technique that enhances user experience by making the app appear more responsive. + +When a Server Action is invoked, the UI is updated immediately to reflect the expected outcome, instead of waiting for the Server Action's response. + +```jsx filename="app/thread.js" +'use client' + +import { experimental_useOptimistic as useOptimistic } from 'react' +import { send } from './actions.js' + +export function Thread({ messages }) { + const [optimisticMessages, addOptimisticMessage] = useOptimistic( + messages, + (state, newMessage) => [...state, { message: newMessage, sending: true }] + ) + const formRef = useRef() + + return ( +
+ {optimisticMessages.map((m) => ( +
+ {m.message} + {m.sending ? 'Sending...' : ''} +
+ ))} +
{ + const message = formData.get('message') + formRef.current.reset() + addOptimisticMessage(message) + await send(message) + }} + ref={formRef} + > + +
+
+ ) +} +``` + +#### Experimental `useFormStatus` + +The **experimental** `useFormStatus` hook can be used within Form Actions, and provides the `pending` property. + +```jsx filename="app/form.js" +'use client' + +import { experimental_useFormStatus as useFormStatus } from 'react-dom' + +function Submit() { + const { pending } = useFormStatus() + + return ( + + Submit + + ) +} +``` + +#### Progressive Enhancement + +Progressive Enhancement allows a `
` to function properly without JavaScript, or with JavaScript disabled. This allows users to interact with the form and submit data even if the JavaScript for the form hasn't been loaded yet or if it fails to load. + +Both Server Form Actions and Client Form Actions support Progressive Enhancement, using one of two strategies: + +- If a **Server Action** is passed directly to a ``, the form is interactive **even if JavaScript is disabled**. +- If a **Client Action** is passed to a ``, the form is still interactive, but the action will be placed in a queue until the form has hydrated. The `` is prioritized with Selective Hydration, so it happens quickly. + +```jsx filename="app/components/example-client-component.js" +'use client' + +import { useState } from 'react' +import { handleSubmit } from './actions.js' + +export default function ExampleClientComponent({ myAction }) { + const [input, setInput] = useState() + + return ( + setInput(e.target.value)}> + {/* ... */} +
+ ) +} +``` + +In both cases, the form is interactive before hydration occurs. Although Server Actions have the additional benefit of not relying on client JavaScript, you can still compose additional behavior with Client Actions where desired without sacrificing interactivity. + +## Examples + +### Usage with Client Components + +#### Import + +Server Actions **cannot** be _defined_ within Client Components, but they can be _imported_. To use Server Actions in Client Components, you can import the action from a file containing a top-level `"use server"` directive. + +```js filename="app/actions.js" highlight={1} +'use server' + +export async function addItem() { + // ... +} +``` + +```jsx filename="app/components/example-client-component.js" highlight={3, 7, 10} +'use client' + +import { useTransition } from 'react' +import { addItem } from '../actions' + +function ExampleClientComponent({ id }) { + let [isPending, startTransition] = useTransition() + + return ( + + ) +} +``` + +#### Props + +Although [importing](#import) Server Actions is recommended, in some cases you might want to pass down a Server Action to a Client Component as a prop. + +For example, you might want to use a dynamically generated value within the action. In that case, passing a Server Action down as a prop might be a viable solution. + +```jsx filename="app/components/example-server-component.js" +import { ExampleClientComponent } from './components/example-client-component.js' + +function ExampleServerComponent({ id }) { + async function updateItem(data) { + 'use server' + modifyItem({ id, data }) + } + + return +} +``` + +```jsx filename="app/components/example-client-component.js" +'use client' + +function ExampleClientComponent({ updateItem }) { + async function action(formData: FormData) { + await updateItem(formData) + } + + return ( +
+ + +
+ ) +} +``` + +### On-demand Revalidation + +Server Actions can be used to revalidate data on-demand by path ([`revalidatePath`](/docs/app/api-reference/functions/revalidatePath)) or by cache tag ([`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)). + +```js +import { revalidateTag } from 'next/cache' + +async function revalidate() { + 'use server' + revalidateTag('blog-posts') +} +``` + +### Validation + +The data passed to a Server Action can be validated or sanitized before invoking the action. For example, you can create a wrapper function that receives the action as its argument, and returns a function that invokes the action if it's valid. + +```js filename="app/actions.js" +'use server' + +import { withValidate } from 'lib/form-validation' + +export const action = withValidate((data) => { + // ... +}) +``` + +```js filename="lib/form-validation.js" +export function withValidate(action) { + return (formData: FormData) => { + 'use server' + + const isValidData = verifyData(formData) + + if (!isValidData) { + throw new Error('Invalid input.') + } + + const data = process(formData) + return action(data) + } +} +``` + +### Using headers + +You can read incoming request headers such as `cookies` and `headers` within a Server Action. + +```js highlight={6} +import { cookies } from 'next/headers' + +async function addItem(data) { + 'use server' + + const cartId = cookies().get('cartId')?.value + + await saveToDb({ cartId, data }) +} +``` + +Additionally, you can modify cookies within a Server Action. + +```js highlight={7, 9, 10, 11, 12, 13, 14} +import { cookies } from 'next/headers'; + +async function create(data) { + 'use server'; + + const cart = await createCart(): + cookies().set('cartId', cart.id) + // or + cookies().set({ + name: 'cartId', + value: cart.id, + httpOnly: true, + path: '/' + }) +} +``` + +## Glossary + +### Actions + +Actions are an experimental feature in React, allowing you to run `async` code in response to a user interaction. + +Actions are not Next.js or React Server Components specific, however, they are not yet available in the stable version of React. When using Actions through Next.js, you are opting into using the React `experimental` channel. + +Actions are defined through the `action` prop on an element. Typically when building HTML forms, you pass a URL to the [`action` prop](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action). With Actions, React now allows you to pass a function directly. + +React also provides built-in solutions for [optimistic updates](#experimental-useoptimistic) with Actions. It's important to note new patterns are still being developed and new APIs may still be added. + +### Form Actions + +[Actions](#actions) integrated into the web standard `
` API, and enable out-of-the-box progressive enhancement and [loading states](#experimental-useformstatus). Similar to the HTML primitive [`formaction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#formation). + +### Server Functions + +Functions that run on the server, but can be called on the client. + +### Server Actions + +[Server Functions](#server-functions) called as an action. + +Server Actions can be progressively enhanced by passing them to a [`form` element's `action` prop](#invocation). The form is interactive before any client-side JavaScript has loaded. This means React hydration is not required for the form to submit. + +### Server Mutations + +[Server Actions](#server-actions) that mutates your data and calls `redirect`, `revalidatePath`, or `revalidateTag`. diff --git a/docs/02-app/01-building-your-application/03-data-fetching/index.mdx b/docs/02-app/01-building-your-application/03-data-fetching/index.mdx new file mode 100644 index 0000000000000..3d2227e9ba699 --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/index.mdx @@ -0,0 +1,163 @@ +--- +title: Data Fetching +description: Learn the fundamentals of data fetching with React and Next.js. +--- + +The Next.js App Router introduces a new, simplified data fetching system built on React and the Web platform. This page will go through the fundamental concepts and patterns to help you manage your data's lifecycle. + +Here's a quick overview of the recommendations on this page: + +1. [Fetch data on the server](#fetching-data-on-the-server) using Server Components. +2. [Fetch data in parallel](#parallel-and-sequential-data-fetching) to minimize waterfalls and reduce loading times. +3. For Layouts and Pages, [fetch data where it's used](#automatic-fetch-request-deduping). Next.js will automatically dedupe requests in a tree. +4. Use [Loading UI, Streaming and Suspense](#streaming-and-suspense) to progressively render a page and show a result to the user while the rest of the content loads. + +## The `fetch()` API + +The new data fetching system is built on top of the native [`fetch()` Web API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and makes use of `async` and `await` in Server Components. + +- React extends `fetch` to provide [automatic request deduping](#automatic-fetch-request-deduping). +- Next.js extends the `fetch` options object to allow each request to set its own [caching and revalidating](/docs/app/building-your-application/data-fetching/caching) rules. + +[Learn how to use `fetch` in Next.js](/docs/app/building-your-application/data-fetching/fetching). + +## Fetching Data on the Server + +Whenever possible, we recommend fetching data in [Server Components](/docs/getting-started/react-essentials#server-components). Server Components **always fetch data on the server**. This allows you to: + +- Have direct access to backend data resources (e.g. databases). +- Keep your application more secure by preventing sensitive information, such as access tokens and API keys, from being exposed to the client. +- Fetch data and render in the same environment. This reduces both the back-and-forth communication between client and server, as well as the work on the main thread on the client. +- Perform multiple data fetches with single round-trip instead of multiple individual requests on the client. +- Reduce client-server [waterfalls](#parallel-and-sequential-data-fetching). +- Depending on your region, data fetching can also happen closer to your data source, reducing latency and improving performance. + +> **Good to know**: It's still possible to fetch data client-side. We recommend using a third-party library such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/v4/) with Client Components. In the future, it'll also be possible to fetch data in Client Components using React's [`use()` hook](/docs/app/building-your-application/data-fetching/fetching#use-in-client-components). + +## Fetching Data at the Component Level + +In the App Router, you can fetch data inside [layouts](/docs/app/building-your-application/routing/pages-and-layouts#layouts), [pages](/docs/app/building-your-application/routing/pages-and-layouts#pages), and components. Data fetching is also compatible with [Streaming and Suspense](#streaming-and-suspense). + +> **Good to know**: For layouts, it's not possible to pass data between a parent layout and its `children` components. We recommend **fetching data directly inside the layout that needs it**, even if you're requesting the same data multiple times in a route. Behind the scenes, React and Next.js will [cache and dedupe](#automatic-fetch-request-deduping) requests to avoid the same data being fetched more than once. + +## Parallel and Sequential Data Fetching + +When fetching data inside components, you need to be aware of two data fetching patterns: Parallel and Sequential. + +Sequential and Parallel Data Fetching + +- With **parallel data fetching**, requests in a route are eagerly initiated and will load data at the same time. This reduces client-server waterfalls and the total time it takes to load data. +- With **sequential data fetching**, requests in a route are dependent on each other and create waterfalls. There may be cases where you want this pattern because one fetch depends on the result of the other, or you want a condition to be satisfied before the next fetch to save resources. However, this behavior can also be unintentional and lead to longer loading times. + +[Learn how to implement parallel and sequential data fetching](/docs/app/building-your-application/data-fetching/fetching#data-fetching-patterns). + +## Automatic `fetch()` Request Deduping + +If you need to fetch the same data (e.g. current user) in multiple components in a tree, Next.js will automatically cache `fetch` requests (`GET`) that have the same input in a temporary cache. This optimization prevents the same data from being fetched more than once during a rendering pass. + +Fetch Request Deduplication + +- On the server, the cache lasts the lifetime of a server request until the rendering process completes. + - This optimization applies to `fetch` requests made in Layouts, Pages, Server Components, `generateMetadata` and `generateStaticParams`. + - This optimization also applies during [static generation](/docs/app/building-your-application/rendering#static-rendering). +- On the client, the cache lasts the duration of a session (which could include multiple client-side re-renders) before a full page reload. + +> **Good to know**: +> +> - `POST` requests are not automatically deduplicated. [Learn more about caching](/docs/app/building-your-application/data-fetching/caching). +> - If you're unable to use `fetch`, React provides a [`cache` function](/docs/app/building-your-application/data-fetching/caching#react-cache) to allow you to manually cache data for the duration of the request. + +## Static and Dynamic Data Fetching + +There are two types of data: **Static** and **Dynamic**. + +- **Static Data** is data that doesn't change often. For example, a blog post. +- **Dynamic Data** is data that changes often or can be specific to users. For example, a shopping cart list. + +Dynamic and Static Data Fetching + +By default, Next.js automatically does static fetches. This means that the data will be fetched at build time, cached, and reused on each request. As a developer, you have control over how the static data is [cached](#caching-data) and [revalidated](#revalidating-data). + +There are two benefits to using static data: + +1. It reduces the load on your database by minimizing the number of requests made. +2. The data is automatically cached for improved loading performance. + +However, if your data is personalized to the user or you want to always fetch the latest data, you can mark requests as _dynamic_ and fetch data on each request without caching. + +[Learn how to do Static and Dynamic data fetching](/docs/app/building-your-application/data-fetching/fetching#static-data-fetching). + +## Caching Data + +Caching is the process of storing data in a location (e.g. [Content Delivery Network](https://vercel.com/docs/concepts/edge-network/overview)) so it doesn't need to be re-fetched from the original source on each request. + +Static Site Generation + +The **Next.js Cache** is a persistent [HTTP cache](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching) that can be globally distributed. This means the cache can scale automatically and be shared across multiple regions depending on your platform (e.g. [Vercel](https://vercel.com/docs/concepts/next.js/overview)). + +Next.js extends the [options object](https://developer.mozilla.org/en-US/docs/Web/API/fetch#:~:text=preflight%20requests.-,cache,-A%20string%20indicating) of the `fetch()` function to allow each request on the server to set its own persistent caching behavior. Together with [component-level data fetching](#fetching-data-at-the-component-level), this allows you to configure caching within your application code directly where the data is being used. + +During server rendering, when Next.js comes across a fetch, it will check the cache to see if the data is already available. If it is, it will return the cached data. If not, it will fetch and store data for future requests. + +> **Good to know**: If you're unable to use `fetch`, React provides a [`cache` function](/docs/app/building-your-application/data-fetching/caching#react-cache) to allow you to manually cache data for the duration of the request. + +[Learn more about caching in Next.js](/docs/app/building-your-application/data-fetching/caching). + +### Revalidating Data + +Revalidation is the process of purging the cache and re-fetching the latest data. This is useful when your data changes and you want to ensure your application shows the latest version without having to rebuild your entire application. + +Next.js provides two types of revalidation: + +- [**Background**](/docs/app/building-your-application/data-fetching/revalidating#background-revalidation): Revalidates the data at a specific time interval. +- [**On-demand**](/docs/app/building-your-application/data-fetching/revalidating#on-demand-revalidation): Revalidates the data whenever there is an update. + +[Learn how to revalidate data](/docs/app/building-your-application/data-fetching/revalidating). + +### Streaming and Suspense + +Streaming and [Suspense](https://react.dev/reference/react/Suspense) are new React features that allow you to progressively render and incrementally stream rendered units of the UI to the client. + +With Server Components and [nested layouts](/docs/app/building-your-application/routing/pages-and-layouts), you're able to instantly render parts of the page that do not specifically require data, and show a [loading state](/docs/app/building-your-application/routing/loading-ui-and-streaming) for parts of the page that are fetching data. This means the user does not have to wait for the entire page to load before they can start interacting with it. + +Server Rendering with Streaming + +To learn more about Streaming and Suspense, see the [Loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming) and [Streaming and Suspense](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) pages. + +## Old Methods + +Previous Next.js data fetching methods such as [`getServerSideProps`](/docs/pages/building-your-application/data-fetching/get-server-side-props), [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props), and [`getInitialProps`](/docs/pages/api-reference/functions/get-initial-props) are **not** supported in the new App Router. However, you can still use them in the [Pages Router](/docs/pages/building-your-application/data-fetching). + +## Next Steps + +Now that you understand the fundamentals of data fetching in Next.js, you can learn more about managing data in your application: diff --git a/docs/02-app/01-building-your-application/04-styling/01-css-modules.mdx b/docs/02-app/01-building-your-application/04-styling/01-css-modules.mdx new file mode 100644 index 0000000000000..31688cef9987a --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/01-css-modules.mdx @@ -0,0 +1,284 @@ +--- +title: CSS Modules +description: Style your Next.js Application with CSS Modules. +--- + + + +
+ Examples + +- [Basic CSS Example](https://github.com/vercel/next.js/tree/canary/examples/basic-css) + +
+ +
+ +Next.js has built-in support for CSS Modules using the `.module.css` extension. + +CSS Modules locally scope CSS by automatically creating a unique class name. This allows you to use the same class name in different files without worrying about collisions. This behavior makes CSS Modules the ideal way to include component-level CSS. + +## Example + + +CSS Modules can be imported into any file inside the `app` directory: + +```tsx filename="app/dashboard/layout.tsx" switcher +import styles from './styles.module.css' + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return
{children}
+} +``` + +```jsx filename="app/dashboard/layout.js" switcher +import styles from './styles.module.css' + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode, +}) { + return
{children}
+} +``` + +```css filename="app/dashboard/styles.module.css" +.dashboard { + padding: 24px; +} +``` + +
+ + + +For example, consider a reusable `Button` component in the `components/` folder: + +First, create `components/Button.module.css` with the following content: + +```css filename="Button.module.css" +/* +You do not need to worry about .error {} colliding with any other `.css` or +`.module.css` files! +*/ +.error { + color: white; + background-color: red; +} +``` + +Then, create `components/Button.js`, importing and using the above CSS file: + +```jsx filename="components/Button.js" +import styles from './Button.module.css' + +export function Button() { + return ( + + ) +} +``` + + + +CSS Modules are an _optional feature_ and are **only enabled for files with the `.module.css` extension**. +Regular `` stylesheets and global CSS files are still supported. + +In production, all CSS Module files will be automatically concatenated into **many minified and code-split** `.css` files. +These `.css` files represent hot execution paths in your application, ensuring the minimal amount of CSS is loaded for your application to paint. + +## Global Styles + + +Global styles can be imported into any layout, page, or component inside the `app` directory. + +> **Good to know**: This is different from the `pages` directory, where you can only import global styles inside the `_app.js` file. + +For example, consider a stylesheet named `app/global.css`: + +```css +body { + padding: 20px 20px 60px; + max-width: 680px; + margin: 0 auto; +} +``` + +Inside the root layout (`app/layout.js`), import the `global.css` stylesheet to apply the styles to every route in your application: + +```tsx filename="app/layout.tsx" switcher +// These styles apply to every route in the application +import './global.css' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +// These styles apply to every route in the application +import './global.css' + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +To add a stylesheet to your application, import the CSS file within `pages/_app.js`. + +For example, consider the following stylesheet named `styles.css`: + +```css filename="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](/docs/pages/building-your-application/routing/custom-app) if not already present. +Then, [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) the `styles.css` file. + +```jsx filename="pages/_app.js" +import '../styles.css' + +// This default export is required in a new `pages/_app.js` file. +export default function MyApp({ Component, pageProps }) { + return +} +``` + +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/pages/building-your-application/routing/custom-app)**. + +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. + + + +## External Stylesheets + + + +Stylesheets published by external packages can be imported anywhere in the `app` directory, including colocated components: + +```tsx filename="app/layout.tsx" switcher +import 'bootstrap/dist/css/bootstrap.css' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import 'bootstrap/dist/css/bootstrap.css' + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +> **Good to know**: External stylesheets must be directly imported from an npm package or downloaded and colocated with your codebase. You cannot use ``. + + + + + +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. + +### 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 filename="pages/_app.js" +import 'bootstrap/dist/css/bootstrap.css' + +export default function MyApp({ Component, pageProps }) { + return +} +``` + +For importing CSS required by a third-party component, you can do so in your component. For example: + +```jsx filename="components/example-dialog.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 ( +
+ + + +

Hello there. I am a dialog

+
+
+ ) +} +``` + +
+ +## Additional Features + +Next.js includes additional features to improve the authoring experience of adding styles: + +- When running locally with `next dev`, local stylesheets (either global or CSS modules) will take advantage of [Fast Refresh](/docs/architecture/fast-refresh) to instantly reflect changes as edits are saved. +- When building for production with `next build`, CSS files will be bundled into fewer minified `.css` files to reduce the number of network requests needed to retrieve styles. +- If you disable JavaScript, styles will still be loaded in the production build (`next start`). However, JavaScript is still required for `next dev` to enable [Fast Refresh](/docs/architecture/fast-refresh). diff --git a/docs/02-app/01-building-your-application/04-styling/02-tailwind-css.mdx b/docs/02-app/01-building-your-application/04-styling/02-tailwind-css.mdx new file mode 100644 index 0000000000000..5338b245d8908 --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/02-tailwind-css.mdx @@ -0,0 +1,124 @@ +--- +title: Tailwind CSS +description: Style your Next.js Application using Tailwind CSS. +--- + + + +
+ Examples + +- [With Tailwind CSS](https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss) + +
+ +
+ +[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that works exceptionally well with Next.js. + +## Installing Tailwind + +Install the Tailwind CSS packages and run the `init` command to generate both the `tailwind.config.js` and `postcss.config.js` files: + +```bash filename="Terminal" +npm install -D tailwindcss postcss autoprefixer +npx tailwindcss init -p +``` + +## Configuring Tailwind + +Inside `tailwind.config.js`, add paths to the files that will use Tailwind CSS class names: + +```js filename="tailwind.config.js" +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './app/**/*.{js,ts,jsx,tsx,mdx}', // Note the addition of the `app` directory. + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + + // Or if using `src` directory: + './src/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: {}, + }, + plugins: [], +} +``` + +You do not need to modify `postcss.config.js`. + +## Importing Styles + +Add the [Tailwind CSS directives](https://tailwindcss.com/docs/functions-and-directives#directives) that Tailwind will use to inject its generated styles to a [Global Stylesheet](/docs/app/building-your-application/styling/css-modules#global-styles) in your application, for example: + +```css filename="app/globals.css" +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +Inside the [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) (`app/layout.tsx`), import the `globals.css` stylesheet to apply the styles to every route in your application. + +```tsx filename="app/layout.tsx" switcher +import type { Metadata } from 'next' + +// These styles apply to every route in the application +import './globals.css' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +// These styles apply to every route in the application +import './globals.css' + +export const metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +## Using Classes + +After installing Tailwind CSS and adding the global styles, you can use Tailwind's utility classes in your application. + +```tsx filename="app/page.tsx" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` + +```jsx filename="app/page.js" switcher +export default function Page() { + return

Hello, Next.js!

+} +``` + +## Usage with Turbopack + +As of Next.js 13.1, Tailwind CSS and PostCSS are supported with [Turbopack](https://turbo.build/pack/docs/features/css#tailwind-css). diff --git a/docs/02-app/01-building-your-application/04-styling/03-css-in-js.mdx b/docs/02-app/01-building-your-application/04-styling/03-css-in-js.mdx new file mode 100644 index 0000000000000..00e1b5387dd3c --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/03-css-in-js.mdx @@ -0,0 +1,308 @@ +--- +title: CSS-in-JS +description: Use CSS-in-JS libraries with Next.js +--- + + + +> **Warning:** CSS-in-JS libraries which require runtime JavaScript are not currently supported in Server Components. Using CSS-in-JS with newer React features like Server Components and Streaming requires library authors to support the latest version of React, including [concurrent rendering](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react). +> +> We're working with the React team on upstream APIs to handle CSS and JavaScript assets with support for React Server Components and streaming architecture. + +The following libraries are supported in Client Components in the `app` directory: + +- [`styled-jsx`](#styled-jsx) +- [`styled-components`](#styled-components) +- [`tamagui`](https://tamagui.dev/docs/guides/next-js#server-components) +- [`style9`](https://github.com/johanholmerin/style9) +- [`vanilla-extract`](https://github.com/vercel/next.js/tree/canary/examples/with-vanilla-extract) + +The following are currently working on support: + +- [`emotion`](https://github.com/emotion-js/emotion/issues/2928) +- [Material UI](https://github.com/mui/material-ui/issues/34905#issuecomment-1401306594) +- [Chakra UI](https://github.com/chakra-ui/chakra-ui/issues/7180) + +> **Good to know**: We're testing out different CSS-in-JS libraries and we'll be adding more examples for libraries that support React 18 features and/or the `app` directory. + +If you want to style Server Components, we recommend using [CSS Modules](/docs/app/building-your-application/styling/css-modules) or other solutions that output CSS files, like PostCSS or [Tailwind CSS](/docs/app/building-your-application/styling/tailwind-css). + +## Configuring CSS-in-JS in `app` + +Configuring CSS-in-JS is a three-step opt-in process that involves: + +1. A **style registry** to collect all CSS rules in a render. +2. The new `useServerInsertedHTML` hook to inject rules before any content that might use them. +3. A Client Component that wraps your app with the style registry during initial server-side rendering. + +### `styled-jsx` + +Using `styled-jsx` in Client Components requires using `v5.1.0`. First, create a new registry: + +```tsx filename="app/registry.tsx" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { StyleRegistry, createStyleRegistry } from 'styled-jsx' + +export default function StyledJsxRegistry({ + children, +}: { + children: React.ReactNode +}) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [jsxStyleRegistry] = useState(() => createStyleRegistry()) + + useServerInsertedHTML(() => { + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return <>{styles} + }) + + return {children} +} +``` + +```jsx filename="app/registry.js" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { StyleRegistry, createStyleRegistry } from 'styled-jsx' + +export default function StyledJsxRegistry({ children }) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [jsxStyleRegistry] = useState(() => createStyleRegistry()) + + useServerInsertedHTML(() => { + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return <>{styles} + }) + + return {children} +} +``` + +Then, wrap your [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) with the registry: + +```tsx filename="app/layout.tsx" switcher +import StyledJsxRegistry from './registry' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import StyledJsxRegistry from './registry' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +[View an example here](https://github.com/vercel/app-playground/tree/main/app/styling/styled-jsx). + +### Styled Components + +Below is an example of how to configure `styled-components@v6.0.0-rc.1` or greater: + +First, use the `styled-components` API to create a global registry component to collect all CSS style rules generated during a render, and a function to return those rules. Then use the `useServerInsertedHTML` hook to inject the styles collected in the registry into the `` HTML tag in the root layout. + +```tsx filename="lib/registry.tsx" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { ServerStyleSheet, StyleSheetManager } from 'styled-components' + +export default function StyledComponentsRegistry({ + children, +}: { + children: React.ReactNode +}) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()) + + useServerInsertedHTML(() => { + const styles = styledComponentsStyleSheet.getStyleElement() + styledComponentsStyleSheet.instance.clearTag() + return <>{styles} + }) + + if (typeof window !== 'undefined') return <>{children} + + return ( + + {children} + + ) +} +``` + +```jsx filename="lib/registry.js" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { ServerStyleSheet, StyleSheetManager } from 'styled-components' + +export default function StyledComponentsRegistry({ children }) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()) + + useServerInsertedHTML(() => { + const styles = styledComponentsStyleSheet.getStyleElement() + styledComponentsStyleSheet.instance.clearTag() + return <>{styles} + }) + + if (typeof window !== 'undefined') return <>{children} + + return ( + + {children} + + ) +} +``` + +Wrap the `children` of the root layout with the style registry component: + +```tsx filename="app/layout.tsx" switcher +import StyledComponentsRegistry from './lib/registry' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import StyledComponentsRegistry from './lib/registry' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +[View an example here](https://github.com/vercel/app-playground/tree/main/app/styling/styled-components). + +> **Good to know**: +> +> - During server rendering, styles will be extracted to a global registry and flushed to the `` of your HTML. This ensures the style rules are placed before any content that might use them. In the future, we may use an upcoming React feature to determine where to inject the styles. +> - During streaming, styles from each chunk will be collected and appended to existing styles. After client-side hydration is complete, `styled-components` will take over as usual and inject any further dynamic styles. +> - We specifically use a Client Component at the top level of the tree for the style registry because it's more efficient to extract CSS rules this way. It avoids re-generating styles on subsequent server renders, and prevents them from being sent in the Server Component payload. + + + + + +
+ Examples + +- [Styled JSX](https://github.com/vercel/next.js/tree/canary/examples/with-styled-jsx) +- [Styled Components](https://github.com/vercel/next.js/tree/canary/examples/with-styled-components) +- [Emotion](https://github.com/vercel/next.js/tree/canary/examples/with-emotion) +- [Linaria](https://github.com/vercel/next.js/tree/canary/examples/with-linaria) +- [Tailwind CSS + Emotion](https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss-emotion) +- [Styletron](https://github.com/vercel/next.js/tree/canary/examples/with-styletron) +- [Cxs](https://github.com/vercel/next.js/tree/canary/examples/with-cxs) +- [Aphrodite](https://github.com/vercel/next.js/tree/canary/examples/with-aphrodite) +- [Fela](https://github.com/vercel/next.js/tree/canary/examples/with-fela) +- [Stitches](https://github.com/vercel/next.js/tree/canary/examples/with-stitches) + +
+ +It's possible to use any existing CSS-in-JS solution.The simplest one is inline styles: + +```jsx +function HiThere() { + return

hi there

+} + +export default HiThere +``` + +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). + +A component using `styled-jsx` looks like this: + +```jsx +function HelloWorld() { + return ( +
+ Hello world +

scoped!

+ + +
+ ) +} + +export default HelloWorld +``` + +Please see the [styled-jsx documentation](https://github.com/vercel/styled-jsx) for more examples. + +### Disabling JavaScript + +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). + +
diff --git a/docs/02-app/01-building-your-application/04-styling/04-sass.mdx b/docs/02-app/01-building-your-application/04-styling/04-sass.mdx new file mode 100644 index 0000000000000..326fc39c49a61 --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/04-sass.mdx @@ -0,0 +1,79 @@ +--- +title: Sass +description: Style your Next.js application using Sass. +--- + +Next.js has built-in support for Sass using both the `.scss` and `.sass` extensions. You can use component-level Sass via CSS Modules and the `.module.scss`or `.module.sass` extension. + +First, install [`sass`](https://github.com/sass/sass): + +```bash filename="Terminal" +npm install --save-dev sass +``` + +> **Good to know**: +> +> Sass supports [two different syntax](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, use `sassOptions` in `next.config.js`. + +```js filename="next.config.js" +const path = require('path') + +module.exports = { + sassOptions: { + includePaths: [path.join(__dirname, 'styles')], + }, +} +``` + +### Sass Variables + +Next.js supports Sass variables exported from CSS Module files. + +For example, using the exported `primaryColor` Sass variable: + +```scss filename="app/variables.module.scss" +$primary-color: #64ff00; + +:export { + primaryColor: $primary-color; +} +``` + + + +```jsx filename="app/page.js" +// maps to root `/` URL + +import variables from './variables.module.scss' + +export default function Page() { + return

Hello, Next.js!

+} +``` + +
+ + + +```jsx filename="pages/_app.js" +import variables from '../styles/variables.module.scss' + +export default function MyApp({ Component, pageProps }) { + return ( + + + + ) +} +``` + + diff --git a/docs/02-app/01-building-your-application/04-styling/index.mdx b/docs/02-app/01-building-your-application/04-styling/index.mdx new file mode 100644 index 0000000000000..c02464f5d6253 --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/index.mdx @@ -0,0 +1,14 @@ +--- +title: Styling +description: Learn the different ways you can style your Next.js application. +--- + +Next.js supports different ways of styling your application, including: + +- **Global CSS**: Simple to use and familiar for those experienced with traditional CSS, but can lead to larger CSS bundles and difficulty managing styles as the application grows. +- **CSS Modules**: Create locally scoped CSS classes to avoid naming conflicts and improve maintainability. +- **Tailwind CSS**: A utility-first CSS framework that allows for rapid custom designs by composing utility classes. +- **Sass**: A popular CSS preprocessor that extends CSS with features like variables, nested rules, and mixins. +- **CSS-in-JS**: Embed CSS directly in your JavaScript components, enabling dynamic and scoped styling. + +Learn more about each approach by exploring their respective documentation: diff --git a/docs/02-app/01-building-your-application/05-optimizing/01-images.mdx b/docs/02-app/01-building-your-application/05-optimizing/01-images.mdx new file mode 100644 index 0000000000000..798f1c6295113 --- /dev/null +++ b/docs/02-app/01-building-your-application/05-optimizing/01-images.mdx @@ -0,0 +1,250 @@ +--- +title: Image Optimization +nav_title: Images +description: Optimize your images with the built-in `next/image` component. +related: + title: API Reference + description: Learn more about the next/image API. + links: + - app/api-reference/components/image +--- + +
+ Examples + +- [Image Component](https://github.com/vercel/next.js/tree/canary/examples/image-component) + +
+ +According to [Web Almanac](https://almanac.httparchive.org), images account for a huge portion of the typical website’s [page weight](https://almanac.httparchive.org/en/2022/page-weight#content-type-and-file-formats) and can have a sizable impact on your website's [LCP performance](https://almanac.httparchive.org/en/2022/performance#lcp-image-optimization). + +The Next.js Image component extends the HTML `` element with features for automatic image optimization: + +- **Size Optimization:** Automatically serve correctly sized images for each device, using modern image formats like WebP and AVIF. +- **Visual Stability:** Prevent [layout shift](/learn/seo/web-performance/cls) automatically when images are loading. +- **Faster Page Loads:** Images are only loaded when they enter the viewport using native browser lazy loading, with optional blur-up placeholders. +- **Asset Flexibility:** On-demand image resizing, even for images stored on remote servers + +## Usage + +```js +import Image from 'next/image' +``` + +You can then define the `src` for your image (either local or remote). + +### Local Images + +To use a local image, `import` your `.jpg`, `.png`, or `.webp` image files. + +Next.js will [automatically determine](#image-sizing) the `width` and `height` of your image based on the imported file. These values are used to prevent [Cumulative Layout Shift](https://nextjs.org/learn/seo/web-performance/cls) while your image is loading. + + + +```jsx filename="app/page.js" +import Image from 'next/image' +import profilePic from './me.png' + +export default function Page() { + return ( + Picture of the author + ) +} +``` + + + + + +```jsx filename="pages/index.js" +import Image from 'next/image' +import profilePic from '../public/me.png' + +export default function Page() { + return ( + Picture of the author + ) +} +``` + + + +> **Warning:** Dynamic `await import()` or `require()` are _not_ supported. The `import` must be static so it can be analyzed at build time. + +### Remote Images + +To use a remote image, the `src` property should be a URL string. + +Since Next.js does not have access to remote files during the build process, you'll need to provide the [`width`](/docs/app/api-reference/components/image#width), [`height`](/docs/app/api-reference/components/image#height) and optional [`blurDataURL`](/docs/app/api-reference/components/image#blurdataurl) props manually. + +The `width` and `height` attributes are used to infer the correct aspect ratio of image and avoid layout shift from the image loading in. The `width` and `height` do _not_ determine the rendered size of the image file. Learn more about [Image Sizing](#image-sizing). + +```jsx filename="app/page.js" +import Image from 'next/image' + +export default function Page() { + return ( + Picture of the author + ) +} +``` + +To safely allow optimizing images, define a list of supported URL patterns in `next.config.js`. Be as specific as possible to prevent malicious usage. For example, the following configuration will only allow images from a specific AWS S3 bucket: + +```js filename="next.config.js" +module.exports = { + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 's3.amazonaws.com', + port: '', + pathname: '/my-bucket/**', + }, + ], + }, +} +``` + +Learn more about [`remotePatterns`](/docs/app/api-reference/components/image#remotepatterns) configuration. If you want to use relative URLs for the image `src`, use a [`loader`](/docs/app/api-reference/components/image#loader). + +### Domains + +Sometimes you may want to optimize a remote image, but still use the built-in Next.js Image Optimization API. To do this, leave the `loader` at its default setting and enter an absolute URL for the Image `src` prop. + +To protect your application from malicious users, you must define a list of remote hostnames you intend to use with the `next/image` component. + +> Learn more about [`remotePatterns`](/docs/app/api-reference/components/image#remotepatterns) configuration. + +### Loaders + +Note that in the [example earlier](#remote-images), a partial URL (`"/me.png"`) is provided for a remote image. This is possible because of the loader architecture. + +A loader is a function that generates the URLs for your image. It modifies the provided `src`, and generates multiple URLs to request the image at different sizes. These multiple URLs are used in the automatic [srcset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset) generation, so that visitors to your site will be served an image that is the right size for their viewport. + +The default loader for Next.js applications uses the built-in Image Optimization API, which optimizes images from anywhere on the web, and then serves them directly from the Next.js web server. If you would like to serve your images directly from a CDN or image server, you can write your own loader function with a few lines of JavaScript. + +You can define a loader per-image with the [`loader` prop](/docs/app/api-reference/components/image#loader), or at the application level with the [`loaderFile` configuration](/docs/app/api-reference/components/image#loaderfile). + +## Priority + +You should add the `priority` property to the image that will be the [Largest Contentful Paint (LCP) element](https://web.dev/lcp/#what-elements-are-considered) for each page. Doing so allows Next.js to specially prioritize the image for loading (e.g. through preload tags or priority hints), leading to a meaningful boost in LCP. + +The LCP element is typically the largest image or text block visible within the viewport of the page. When you run `next dev`, you'll see a console warning if the LCP element is an `` without the `priority` property. + +Once you've identified the LCP image, you can add the property like this: + + + +```jsx filename="app/page.js" +import Image from 'next/image' + +export default function Home() { + return ( + <> +

My Homepage

+ Picture of the author +

Welcome to my homepage!

+ + ) +} +``` + +
+ + + +```jsx filename="app/page.js" +import Image from 'next/image' +import profilePic from '../public/me.png' + +export default function Page() { + return Picture of the author +} +``` + + + +See more about priority in the [`next/image` component documentation](/docs/app/api-reference/components/image#priority). + +## Image Sizing + +One of the ways that images most commonly hurt performance is through _layout shift_, where the image pushes other elements around on the page as it loads in. This performance problem is so annoying to users that it has its own Core Web Vital, called [Cumulative Layout Shift](https://web.dev/cls/). The way to avoid image-based layout shifts is to [always size your images](https://web.dev/optimize-cls/#images-without-dimensions). This allows the browser to reserve precisely enough space for the image before it loads. + +Because `next/image` is designed to guarantee good performance results, it cannot be used in a way that will contribute to layout shift, and **must** be sized in one of three ways: + +1. Automatically, using a [static import](#local-images) +2. Explicitly, by including a [`width`](/docs/app/api-reference/components/image#width) and [`height`](/docs/app/api-reference/components/image#height) property +3. Implicitly, by using [fill](/docs/app/api-reference/components/image#fill) which causes the image to expand to fill its parent element. + +> **What if I don't know the size of my images?** +> +> If you are accessing images from a source without knowledge of the images' sizes, there are several things you can do: +> +> **Use `fill`** +> +> The [`fill`](/docs/app/api-reference/components/image#fill) prop allows your image to be sized by its parent element. Consider using CSS to give the image's parent element space on the page along [`sizes`](/docs/app/api-reference/components/image#sizes) prop to match any media query break points. You can also use [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) with `fill`, `contain`, or `cover`, and [`object-position`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) to define how the image should occupy that space. +> +> **Normalize your images** +> +> If you're serving images from a source that you control, consider modifying your image pipeline to normalize the images to a specific size. +> +> **Modify your API calls** +> +> If your application is retrieving image URLs using an API call (such as to a CMS), you may be able to modify the API call to return the image dimensions along with the URL. + +If none of the suggested methods works for sizing your images, the `next/image` component is designed to work well on a page alongside standard `` elements. + +## Styling + +Styling the Image component is similar to styling a normal `` element, but there are a few guidelines to keep in mind: + +- Use `className` or `style`, not `styled-jsx`. + - In most cases, we recommend using the `className` prop. This can be an imported [CSS Module](/docs/app/building-your-application/styling/css-modules), a [global stylesheet](/docs/app/building-your-application/styling/css-modules#global-styles), etc. + - You can also use the `style` prop to assign inline styles. + - You cannot use [styled-jsx](/docs/app/building-your-application/styling/css-in-js) because it's scoped to the current component (unless you mark the style as `global`). +- When using `fill`, the parent element must have `position: relative` + - This is necessary for the proper rendering of the image element in that layout mode. +- When using `fill`, the parent element must have `display: block` + - This is the default for `
` elements but should be specified otherwise. + +For examples, see the [Image Component Demo](https://image-component.nextjs.gallery). + +### Examples + +For examples of the Image component used with the various styles, see the [Image Component Demo](https://image-component.nextjs.gallery). + +## Other Properties + +[**View all properties available to the `next/image` component.**](/docs/app/api-reference/components/image) + +## Configuration + +The `next/image` component and Next.js Image Optimization API can be configured in the [`next.config.js` file](/docs/app/api-reference/next-config-js). These configurations allow you to [enable remote images](/docs/app/api-reference/components/image#remotepatterns), [define custom image breakpoints](/docs/app/api-reference/components/image#devicesizes), [change caching behavior](/docs/app/api-reference/components/image#caching-behavior) and more. + +[**Read the full image configuration documentation for more information.**](/docs/app/api-reference/components/image#configuration-options) diff --git a/docs/02-app/01-building-your-application/05-optimizing/02-fonts.mdx b/docs/02-app/01-building-your-application/05-optimizing/02-fonts.mdx new file mode 100644 index 0000000000000..67b7eddf4b497 --- /dev/null +++ b/docs/02-app/01-building-your-application/05-optimizing/02-fonts.mdx @@ -0,0 +1,639 @@ +--- +title: Font Optimization +nav_title: Fonts +description: Optimize your application's web fonts with the built-in `next/font` loaders. +related: + title: API Reference + description: Learn more about the next/font API. + links: + - app/api-reference/components/font +--- + +[**`next/font`**](/docs/app/api-reference/components/font) will automatically optimize your fonts (including custom fonts) and remove external network requests for improved privacy and performance. + +> **🎥 Watch:** Learn more about how to use `next/font` → [YouTube (6 minutes)](https://www.youtube.com/watch?v=L8_98i_bMMA). + +`next/font` includes **built-in automatic self-hosting** for _any_ font file. This means you can optimally load web fonts with zero layout shift, thanks to the underlying CSS `size-adjust` property used. + +This new font system also allows you to conveniently use all Google Fonts with performance and privacy in mind. CSS and font files are downloaded at build time and self-hosted with the rest of your static assets. **No requests are sent to Google by the browser.** + +## Google Fonts + +Automatically self-host any Google Font. Fonts are included in the deployment and served from the same domain as your deployment. **No requests are sent to Google by the browser.** + +Get started by importing the font you would like to use from `next/font/google` as a function. We recommend using [variable fonts](https://fonts.google.com/variablefonts) for the best performance and flexibility. + + + +```tsx filename="app/layout.tsx" switcher +import { Inter } from 'next/font/google' + +// If loading a variable font, you don't need to specify the font weight +const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Inter } from 'next/font/google' + +// If loading a variable font, you don't need to specify the font weight +const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +If you can't use a variable font, you will **need to specify a weight**: + +```tsx filename="app/layout.tsx" switcher +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +To use the font in all your pages, add it to [`_app.js` file](/docs/pages/building-your-application/routing/custom-app) under `/pages` as shown below: + +```jsx filename="pages/_app.js" +import { Inter } from 'next/font/google' + +// If loading a variable font, you don't need to specify the font weight +const inter = Inter({ subsets: ['latin'] }) + +export default function MyApp({ Component, pageProps }) { + return ( +
+ +
+ ) +} +``` + +If you can't use a variable font, you will **need to specify a weight**: + +```jsx filename="pages/_app.js" +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], +}) + +export default function MyApp({ Component, pageProps }) { + return ( +
+ +
+ ) +} +``` + +
+ +You can specify multiple weights and/or styles by using an array: + +```jsx filename="app/layout.js" +const roboto = Roboto({ + weight: ['400', '700'], + style: ['normal', 'italic'], + subsets: ['latin'], + display: 'swap', +}) +``` + +> **Good to know**: Use an underscore (\_) for font names with multiple words. E.g. `Roboto Mono` should be imported as `Roboto_Mono`. + + + +### Apply the font in `` + +You can also use the font without a wrapper and `className` by injecting it inside the `` as follows: + +```jsx filename="pages/_app.js" +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export default function MyApp({ Component, pageProps }) { + return ( + <> + + + + ) +} +``` + +### Single page usage + +To use the font on a single page, add it to the specific page as shown below: + +```jsx filename="pages/index.js" +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export default function Home() { + return ( +
+

Hello World

+
+ ) +} +``` + +
+ +### Specifying a subset + +Google Fonts are automatically [subset](https://fonts.google.com/knowledge/glossary/subsetting). This reduces the size of the font file and improves performance. You'll need to define which of these subsets you want to preload. Failing to specify any subsets while [`preload`](/docs/app/api-reference/components/font#preload) is `true` will result in a warning. + +This can be done by adding it to the function call: + + + +```tsx filename="app/layout.tsx" switcher +const inter = Inter({ subsets: ['latin'] }) +``` + +```jsx filename="app/layout.js" switcher +const inter = Inter({ subsets: ['latin'] }) +``` + + + + + +```jsx filename="pages/_app.js" +const inter = Inter({ subsets: ['latin'] }) +``` + + + +View the [Font API Reference](/docs/02-app/02-api-reference/01-components/font.mdx) for more information. + +### Using Multiple Fonts + +You can import and use multiple fonts in your application. There are two approaches you can take. + +The first approach is to create a utility function that exports a font, imports it, and applies its `className` where needed. This ensures the font is preloaded only when it's rendered: + +```ts filename="app/fonts.ts" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +export const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', +}) +``` + +```js filename="app/fonts.js" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +export const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', +}) +``` + + + +```tsx filename="app/layout.tsx" switcher +import { inter } from './fonts' + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + +
{children}
+ + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { inter } from './fonts' + +export default function Layout({ children }) { + return ( + + +
{children}
+ + + ) +} +``` + +```tsx filename="app/page.tsx" switcher +import { roboto_mono } from './fonts' + +export default function Page() { + return ( + <> +

My page

+ + ) +} +``` + +```jsx filename="app/page.js" switcher +import { roboto_mono } from './fonts' + +export default function Page() { + return ( + <> +

My page

+ + ) +} +``` + +
+ +In the example above, `Inter` will be applied globally, and `Roboto Mono` can be imported and applied as needed. + +Alternatively, you can create a [CSS variable](/docs/app/api-reference/components/font#variable) and use it with your preferred CSS solution: + + + +```tsx filename="app/layout.tsx" switcher +import { Inter, Roboto_Mono } from 'next/font/google' +import styles from './global.css' + +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', + display: 'swap', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + variable: '--font-roboto-mono', + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + +

My App

+
{children}
+ + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', + display: 'swap', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + variable: '--font-roboto-mono', + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + +

My App

+
{children}
+ + + ) +} +``` + +
+ +```css filename="app/global.css" +html { + font-family: var(--font-inter); +} + +h1 { + font-family: var(--font-roboto-mono); +} +``` + +In the example above, `Inter` will be applied globally, and any `

` tags will be styled with `Roboto Mono`. + +> **Recommendation**: Use multiple fonts conservatively since each new font is an additional resource the client has to download. + +## Local Fonts + +Import `next/font/local` and specify the `src` of your local font file. We recommend using [variable fonts](https://fonts.google.com/variablefonts) for the best performance and flexibility. + + + +```tsx filename="app/layout.tsx" switcher +import localFont from 'next/font/local' + +// Font files can be colocated inside of `app` +const myFont = localFont({ + src: './my-font.woff2', + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import localFont from 'next/font/local' + +// Font files can be colocated inside of `app` +const myFont = localFont({ + src: './my-font.woff2', + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +```jsx filename="pages/_app.js" +import localFont from 'next/font/local' + +// Font files can be colocated inside of `pages` +const myFont = localFont({ src: './my-font.woff2' }) + +export default function MyApp({ Component, pageProps }) { + return ( +
+ +
+ ) +} +``` + +
+ +If you want to use multiple files for a single font family, `src` can be an array: + +```js +const roboto = localFont({ + src: [ + { + path: './Roboto-Regular.woff2', + weight: '400', + style: 'normal', + }, + { + path: './Roboto-Italic.woff2', + weight: '400', + style: 'italic', + }, + { + path: './Roboto-Bold.woff2', + weight: '700', + style: 'normal', + }, + { + path: './Roboto-BoldItalic.woff2', + weight: '700', + style: 'italic', + }, + ], +}) +``` + +View the [Font API Reference](/docs/app/api-reference/components/font) for more information. + +## With Tailwind CSS + +`next/font` can be used with [Tailwind CSS](https://tailwindcss.com/) through a [CSS variable](/docs/app/api-reference/components/font#css-variables). + +In the example below, we use the font `Inter` from `next/font/google` (you can use any font from Google or Local Fonts). Load your font with the `variable` option to define your CSS variable name and assign it to `inter`. Then, use `inter.variable` to add the CSS variable to your HTML document. + + + +```tsx filename="app/layout.tsx" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + display: 'swap', + variable: '--font-inter', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', + variable: '--font-roboto-mono', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + display: 'swap', + variable: '--font-inter', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', + variable: '--font-roboto-mono', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +```jsx filename="pages/_app.js" +import { Inter } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', +}) + +export default function MyApp({ Component, pageProps }) { + return ( +
+ +
+ ) +} +``` + +
+ +Finally, add the CSS variable to your [Tailwind CSS config](/docs/app/building-your-application/styling/tailwind-css#configuring-tailwind): + +```js filename="tailwind.config.js" +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './pages/**/*.{js,ts,jsx,tsx}', + './components/**/*.{js,ts,jsx,tsx}', + ], + theme: { + extend: { + fontFamily: { + sans: ['var(--font-inter)'], + mono: ['var(--font-roboto-mono)'], + }, + }, + }, + plugins: [], +} +``` + +You can now use the `font-sans` and `font-mono` utility classes to apply the font to your elements. + +## Preloading + + +When a font function is called on a page of your site, it is not globally available and preloaded on all routes. Rather, the font is only preloaded on the related routes based on the type of file where it is used: + +- If it's a [unique page](/docs/app/building-your-application/routing/pages-and-layouts#pages), it is preloaded on the unique route for that page. +- If it's a [layout](/docs/app/building-your-application/routing/pages-and-layouts#layouts), it is preloaded on all the routes wrapped by the layout. +- If it's the [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required), it is preloaded on all routes. + + + + + +When a font function is called on a page of your site, it is not globally available and preloaded on all routes. Rather, the font is only preloaded on the related route/s based on the type of file where it is used: + +- if it's a [unique page](/docs/pages/building-your-application/routing/pages-and-layouts), it is preloaded on the unique route for that page +- if it's in the [custom App](/docs/pages/building-your-application/routing/custom-app), it is preloaded on all the routes of the site under `/pages` + + + +## Reusing fonts + +Every time you call the `localFont` or Google font function, that font is hosted as one instance in your application. Therefore, if you load the same font function in multiple files, multiple instances of the same font are hosted. In this situation, it is recommended to do the following: + +- Call the font loader function in one shared file +- Export it as a constant +- Import the constant in each file where you would like to use this font diff --git a/docs/02-app/01-building-your-application/05-optimizing/03-scripts.mdx b/docs/02-app/01-building-your-application/05-optimizing/03-scripts.mdx new file mode 100644 index 0000000000000..6fde53d58c014 --- /dev/null +++ b/docs/02-app/01-building-your-application/05-optimizing/03-scripts.mdx @@ -0,0 +1,373 @@ +--- +title: Script Optimization +nav_title: Scripts +description: Optimize 3rd party scripts with the built-in Script component. +related: + title: API Reference + description: Learn more about the next/script API. + links: + - app/api-reference/components/script +--- + + + +### Layout Scripts + +To load a third-party script for multiple routes, import `next/script` and include the script directly in your layout component: + +```tsx filename="app/dashboard/layout.tsx" switcher +import Script from 'next/script' + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + <> +
{children}
+ +``` + +Or by using the `dangerouslySetInnerHTML` property: + +```jsx + -``` - -Or by using the `dangerouslySetInnerHTML` property: - -```jsx -