diff --git a/.eslintignore b/.eslintignore index 22d834344e38..d168d82d7a56 100644 --- a/.eslintignore +++ b/.eslintignore @@ -12,6 +12,7 @@ examples/with-jest/** examples/with-mobx-state-tree/** examples/with-mobx/** packages/next/bundles/webpack/packages/*.runtime.js +packages/next/bundles/webpack/packages/lazy-compilation-*.js packages/next/compiled/**/* packages/react-refresh-utils/**/*.js packages/react-dev-overlay/lib/** @@ -24,5 +25,9 @@ packages/next-codemod/**/*.d.ts packages/next-env/**/*.d.ts packages/create-next-app/templates/** test/integration/eslint/** +test/development/basic/legacy-decorators/**/* +test/production/emit-decorator-metadata/**/*.js test-timings.json -packages/next/build/swc/tests/fixture/** +packages/next-swc/crates/** +bench/nested-deps/pages/** +bench/nested-deps/components/** diff --git a/.eslintrc.json b/.eslintrc.json index 1cee5507a590..26824c94ba66 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -30,14 +30,15 @@ }, "overrides": [ { - "files": ["test/**/*.test.js"], + "files": ["test/**/*.js", "test/**/*.ts", "**/*.test.ts"], "extends": ["plugin:jest/recommended"], "rules": { "jest/expect-expect": "off", "jest/no-disabled-tests": "off", "jest/no-conditional-expect": "off", "jest/valid-title": "off", - "jest/no-interpolation-in-snapshots": "off" + "jest/no-interpolation-in-snapshots": "off", + "jest/no-export": "off" } }, { "files": ["**/__tests__/**"], "env": { "jest": true } }, diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..08f58f77658a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +packages/next/bundles/** -text +packages/next/compiled/** -text diff --git a/.github/.kodiak.toml b/.github/.kodiak.toml index a90b3113f653..ee535afe225c 100644 --- a/.github/.kodiak.toml +++ b/.github/.kodiak.toml @@ -13,6 +13,7 @@ notify_on_conflict = false [merge.message] title = "pull_request_title" body = "pull_request_body" +include_coauthors= true include_pr_number = true body_type = "markdown" strip_html_comments = true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5b64af494dbb..8d45c2a88972 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,24 @@ # Learn how to add code owners here: # https://help.github.com/en/articles/about-code-owners -* @timneutkens @ijjk @shuding @styfle @huozhi @padmaia -/docs/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @leerob @lfades -/examples/ @timneutkens @ijjk @shuding @leerob @lfades +* @timneutkens @ijjk @shuding @huozhi +/.github/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia +/docs/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @leerob @lfades @molebox +/examples/ @timneutkens @ijjk @shuding @leerob @lfades @steven-tey + +# SWC Build (@padmaia) + +/packages/next/build/ @timneutkens @ijjk @shuding @padmaia @huozhi + +# Image Component (@styfle) + +/examples/image-component/ @timneutkens @ijjk @shuding @styfle +/packages/next/build/webpack/loaders/next-image-loader.js @timneutkens @ijjk @shuding @styfle +/packages/next/client/image.tsx @timneutkens @ijjk @shuding @styfle +/packages/next/image-types/ @timneutkens @ijjk @shuding @styfle +/packages/next/server/image-config.ts @timneutkens @ijjk @shuding @styfle +/packages/next/server/image-optimizer.ts @timneutkens @ijjk @shuding @styfle +/packages/next/server/serve-static.ts @timneutkens @ijjk @shuding @styfle +/packages/next/server/config.ts @timneutkens @ijjk @shuding @styfle +/test/integration/image-optimizer/ @timneutkens @ijjk @shuding @styfle +/test/integration/image-component/ @timneutkens @ijjk @shuding @styfle diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml index 440f9434bf33..d0aa0b7589ac 100644 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yml +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -14,6 +14,12 @@ body: - type: markdown attributes: value: 'next@canary is the beta version of Next.js. It includes all features and fixes that are pending to land on the stable release line.' + - type: textarea + attributes: + label: Run `next info` (available from version 12.0.8 and up) + description: Please run `next info` in the root directory of your project and paste the results. You might need to use `npx --no-install next info` if next is not in the current PATH. + validations: + required: false - type: input attributes: label: What version of Next.js are you using? diff --git a/.github/ISSUE_TEMPLATE/4.docs_request.yml b/.github/ISSUE_TEMPLATE/4.docs_request.yml new file mode 100644 index 000000000000..a2fe06929844 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4.docs_request.yml @@ -0,0 +1,24 @@ +name: 'Docs Request for an Update or Improvement' +description: A request to update or improve Next.js documentation +title: 'Docs: ' +labels: + - 'template: documentation' +body: + - type: textarea + attributes: + label: What is the improvement or update you wish to see? + description: 'Example: I would like to see more examples of how to use the `` component. Or, the `` component docs are missing information.' + validations: + required: true + - type: textarea + attributes: + label: Is there any context that might help us understand? + description: A clear description of any added context that might help us understand. + validations: + required: true + - type: input + attributes: + label: Does the docs page already exist? Please link to it. + description: 'Example: https://nextjs.org/docs/api-reference/next/link' + validations: + required: false diff --git a/.github/actions/next-stats-action/src/index.js b/.github/actions/next-stats-action/src/index.js index 64b1b1c18e05..52b9fd32ab91 100644 --- a/.github/actions/next-stats-action/src/index.js +++ b/.github/actions/next-stats-action/src/index.js @@ -45,6 +45,10 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { await checkoutRef(actionInfo.prRef, diffRepoDir) } + if (actionInfo.isRelease) { + process.env.STATS_IS_RELEASE = 'true' + } + // load stats config from allowed locations const { statsConfig, relativeStatsAppDir } = loadStatsConfig() @@ -114,6 +118,15 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { // in case of noisy environment slowing down initial repo build await exec(buildCommand, false, { timeout: 5 * 60 * 1000 }) } + await fs.copy( + path.join(__dirname, '../native'), + path.join(dir, 'packages/next-swc/native') + ) + // TODO: remove after next stable release (current v12.0.4) + await fs.copy( + path.join(__dirname, '../native'), + path.join(dir, 'packages/next/native') + ) logger(`Linking packages in ${dir}`) const pkgPaths = await linkPackages(dir) 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 534ab34229e5..d06ce49d2dc1 100644 --- a/.github/actions/next-stats-action/src/prepare/repo-setup.js +++ b/.github/actions/next-stats-action/src/prepare/repo-setup.js @@ -73,6 +73,10 @@ module.exports = (actionInfo) => { const packedPkgPath = path.join(pkgPath, `${pkg}-packed.tgz`) const pkgDataPath = path.join(pkgPath, 'package.json') + if (!fs.existsSync(pkgDataPath)) { + console.log(`Skipping ${pkgDataPath}`) + continue + } const pkgData = require(pkgDataPath) const { name } = pkgData pkgDatas.set(name, { @@ -93,6 +97,25 @@ module.exports = (actionInfo) => { if (!pkgData.dependencies || !pkgData.dependencies[pkg]) continue pkgData.dependencies[pkg] = packedPkgPath } + // make sure native binaries are included in local linking + if (pkg === '@next/swc') { + if (!pkgData.files) { + pkgData.files = [] + } + pkgData.files.push('native') + console.log( + 'using swc binaries: ', + await exec(`ls ${path.join(path.dirname(pkgDataPath), 'native')}`) + ) + } + if (pkg === 'next') { + if (pkgDatas.get('@next/swc')) { + pkgData.dependencies['@next/swc'] = + pkgDatas.get('@next/swc').packedPkgPath + } else { + pkgData.files.push('native') + } + } await fs.writeFile( pkgDataPath, JSON.stringify(pkgData, null, 2), @@ -104,7 +127,7 @@ module.exports = (actionInfo) => { // to the correct versions for (const pkgName of pkgDatas.keys()) { const { pkg, pkgPath } = pkgDatas.get(pkgName) - await exec(`cd ${pkgPath} && yarn pack -f ${pkg}-packed.tgz`) + await exec(`cd ${pkgPath} && yarn pack -f ${pkg}-packed.tgz`, true) } 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 e2159e9a89bf..f0dc07bd87a0 100644 --- a/.github/actions/next-stats-action/src/run/index.js +++ b/.github/actions/next-stats-action/src/run/index.js @@ -67,7 +67,7 @@ async function runConfigs( const results = await glob(rename.srcGlob, { cwd: statsAppDir }) for (const result of results) { let dest = rename.removeHash - ? result.replace(/(\.|-)[0-9a-f]{20}(\.|-)/g, '$1HASH$2') + ? result.replace(/(\.|-)[0-9a-f]{16}(\.|-)/g, '$1HASH$2') : rename.dest if (result === dest) continue await fs.move( diff --git a/.github/labeler.json b/.github/labeler.json index 1499d280fb29..ead33c713c82 100644 --- a/.github/labeler.json +++ b/.github/labeler.json @@ -1,13 +1,17 @@ { "labels": { - "type: example": ["examples/**"], - "type: documentation": ["docs/**", "errors/**"], - "type: create-next-app": ["packages/create-next-app/**"], + "area: examples": ["examples/**"], + "area: documentation": ["docs/**", "errors/**"], + "area: create-next-app": ["packages/create-next-app/**"], "type: next": [ "packages/next/**", "packages/react-dev-overlay/**", "packages/react-refresh-utils/**", - "packages/next-codemod/**" + "packages/next-codemod/**", + "packages/eslint-plugin-next/**", + "packages/eslint-config-next/**", + "packages/next-env/**", + "packages/next-swc/**" ], "created-by: Chrome Aurora": [ { "type": "user", "pattern": "spanicker" }, @@ -26,7 +30,13 @@ { "type": "user", "pattern": "sokra" }, { "type": "user", "pattern": "styfle" }, { "type": "user", "pattern": "leerob" }, + { "type": "user", "pattern": "kdy1" }, { "type": "user", "pattern": "timneutkens" } + ], + "created-by: Next.js docs team": [ + { "type": "user", "pattern": "MaedahBatool" }, + { "type": "user", "pattern": "molebox" }, + { "type": "user", "pattern": "ismaelrumzan" } ] } } diff --git a/.github/lock.yml b/.github/lock.yml deleted file mode 100644 index a836a1e1e94a..000000000000 --- a/.github/lock.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Configuration for lock-threads - https://github.com/dessant/lock-threads - -# Number of days of inactivity before a closed issue or pull request is locked -daysUntilLock: 365 -# Comment to post before locking. Set to `false` to disable -lockComment: false diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9e9b995c92dd..dd585c536542 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -21,4 +21,4 @@ Choose the right checklist for the change that you're making: ## Documentation / Examples -- [ ] Make sure the linting passes +- [ ] Make sure the linting passes by running `yarn lint` diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml deleted file mode 100644 index dcf21e7da8fd..000000000000 --- a/.github/workflows/build_native.yml +++ /dev/null @@ -1,90 +0,0 @@ -on: workflow_dispatch - -name: Build next-swc native binaries - -jobs: - build-native: - strategy: - matrix: - os: [ubuntu-18.04, macos-latest, windows-latest] - description: [default] - include: - - os: ubuntu-18.04 - target: x86_64-unknown-linux-gnu - - os: windows-latest - target: x86_64-pc-windows-msvc - - os: macos-latest - target: x86_64-apple-darwin - - os: macos-latest - target: aarch64-apple-darwin - description: m1 - - name: next-swc - ${{ matrix.os }} - ${{ matrix.target }} - node@14 - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v2 - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: 14 - check-latest: true - - name: Install - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2021-03-25 - target: ${{ matrix.target }} - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: stable-${{ matrix.os }}-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: stable-${{ matrix.os }}-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache native binary - id: binary-cache - uses: actions/cache@v2 - with: - path: packages/next/native/** - key: next-swc-nightly-2021-03-25-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_native.yml', 'packages/next/build/swc/**') }} - - name: Cross build aarch64 setup - if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} - run: | - sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; - export CC=$(xcrun -f clang); - export CXX=$(xcrun -f clang++); - SYSROOT=$(xcrun --sdk macosx --show-sdk-path); - export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; - - name: 'Build' - if: steps.binary-cache.outputs.cache-hit != true - run: yarn build-native --target ${{ matrix.target }} - env: - MACOSX_DEPLOYMENT_TARGET: '10.13' - working-directory: packages/next - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: next-swc-binaries - path: packages/next/native/next-swc.*.node - - name: Clear the cargo caches - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache - commit: - needs: build-native - runs-on: ubuntu-18.04 - - steps: - - uses: actions/checkout@v2 - - uses: actions/download-artifact@v2 - with: - name: next-swc-binaries - path: packages/next/native - - uses: EndBug/add-and-commit@v7 - with: - add: 'packages/next/native --force' - message: 'Build next-swc binaries' diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index 3a00a6f0f0ee..7906496c0d9a 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -1,7 +1,6 @@ on: push: branches: [canary] - tags: [v*] pull_request: types: [opened, synchronize] @@ -22,9 +21,20 @@ jobs: runs-on: ubuntu-latest env: NEXT_TELEMETRY_DISABLED: 1 + # we build a dev binary for use in CI so skip downloading + # canary next-swc binaries in the monorepo + NEXT_SKIP_NATIVE_POSTINSTALL: 1 outputs: docsChange: ${{ steps.docs-change.outputs.DOCS_CHANGE }} + isRelease: ${{ steps.check-release.outputs.IS_RELEASE }} + weekNum: ${{ steps.get-week.outputs.WEEK }} steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + - uses: actions/checkout@v2 with: fetch-depth: 25 @@ -33,93 +43,138 @@ jobs: - name: tune linux network run: sudo ethtool -K eth0 tx off rx off - - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - run: yarn install --frozen-lockfile --check-files - run: node run-tests.js --timings --write-timings -g 1/1 + - run: node ./scripts/fetch-tags.mjs ${{ github.sha }} + - name: Check docs only change run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') id: docs-change + - run: echo ${{steps.docs-change.outputs.DOCS_CHANGE}} + - id: check-release + run: | + if [[ $(git describe --exact-match 2> /dev/null || :) = v* ]]; + then + echo "::set-output name=IS_RELEASE::true" + else + echo "::set-output name=IS_RELEASE::false" + fi + # We use week in the turbo cache key to keep the cache from infinitely growing + - id: get-week + run: echo ::set-output name=WEEK::$(date +%U) + - uses: actions/cache@v2 id: cache-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} lint: runs-on: ubuntu-latest needs: build steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + - uses: actions/cache@v2 id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} - run: ./scripts/check-manifests.js - run: yarn lint checkPrecompiled: name: Check Pre-compiled runs-on: ubuntu-latest - needs: [build, build-native] + needs: build env: NEXT_TELEMETRY_DISABLED: 1 steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + # https://github.com/actions/virtual-environments/issues/1187 - name: tune linux network + if: ${{needs.build.outputs.docsChange != 'docs only change'}} run: sudo ethtool -K eth0 tx off rx off + - uses: actions/checkout@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - run: mv .git .git-bak + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + - uses: actions/cache@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: path: ./* - key: ${{ github.sha }} - - uses: actions/download-artifact@v2 - if: ${{needs.build.outputs.docsChange != 'docs only change'}} - with: - name: next-swc-binaries - path: packages/next/build/swc/dist - # Only check linux build for now, mac builds can sometimes be different even with the same code - - run: | - mv ./packages/next/build/swc/dist/next-swc.linux-x64-gnu.node \ - ./packages/next/native/next-swc.linux-x64-gnu.node + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - run: rm -rf .git && mv .git-bak .git if: ${{needs.build.outputs.docsChange != 'docs only change'}} + - run: ./scripts/check-pre-compiled.sh if: ${{needs.build.outputs.docsChange != 'docs only change'}} + - uses: EndBug/add-and-commit@v7 + if: ${{ failure() }} + with: + add: 'packages/next/compiled packages/next/bundles --force' + message: '⚙ Update compiled files' + testUnit: name: Test Unit runs-on: ubuntu-latest - needs: build + needs: [build, build-native-dev] env: NEXT_TELEMETRY_DISABLED: 1 NEXT_TEST_JOB: 1 - HEADLESS: true steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + - uses: actions/cache@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native - run: node run-tests.js --type unit if: ${{needs.build.outputs.docsChange != 'docs only change'}} - testIntegration: - name: Test Integration + testDev: + name: Test Development runs-on: ubuntu-latest - needs: build + needs: [build, build-native-dev] env: NEXT_TELEMETRY_DISABLED: 1 NEXT_TEST_JOB: 1 - HEADLESS: true - strategy: - fail-fast: false - matrix: - group: [1, 2, 3, 4, 5, 6] steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + - run: echo ${{needs.build.outputs.docsChange}} # https://github.com/actions/virtual-environments/issues/1187 @@ -131,121 +186,233 @@ jobs: id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} - # TODO: remove after we fix watchpack watching too much - - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + - uses: actions/download-artifact@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native - - run: xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/6 -c 3 + - run: npm i -g playwright-chromium@1.14.1 && npx playwright install-deps if: ${{needs.build.outputs.docsChange != 'docs only change'}} - testElectron: - name: Test Electron + - run: node run-tests.js --type development + name: Run test/development + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - run: NEXT_TEST_MODE=dev node run-tests.js --type e2e + name: Run test/e2e (dev) + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - name: Upload test trace + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-trace + if-no-files-found: ignore + retention-days: 2 + path: | + test/traces + + testProd: + name: Test Production runs-on: ubuntu-latest - needs: build + needs: [build, build-native-dev] env: NEXT_TELEMETRY_DISABLED: 1 NEXT_TEST_JOB: 1 - HEADLESS: true - TEST_ELECTRON: 1 steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + - run: echo ${{needs.build.outputs.docsChange}} + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + - uses: actions/cache@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native - # TODO: remove after we fix watchpack watching too much - - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + - run: npm i -g playwright-chromium@1.14.1 && npx playwright install-deps if: ${{needs.build.outputs.docsChange != 'docs only change'}} - - run: cd test/integration/with-electron/app && yarn + - run: node run-tests.js --type production + name: Run test/production if: ${{needs.build.outputs.docsChange != 'docs only change'}} - - run: xvfb-run node run-tests.js test/integration/with-electron/test/index.test.js + - run: NEXT_TEST_MODE=start node run-tests.js --type e2e + name: Run test/e2e (production) if: ${{needs.build.outputs.docsChange != 'docs only change'}} - testYarnPnP: + testIntegration: + name: Test Integration runs-on: ubuntu-latest - needs: build + needs: [build, build-native-dev] env: - NODE_OPTIONS: '--unhandled-rejections=strict' - YARN_COMPRESSION_LEVEL: '0' + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} + strategy: + fail-fast: false + matrix: + group: [1, 2, 3, 4, 5, 6] steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + + - run: echo ${{needs.build.outputs.docsChange}} + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + - uses: actions/cache@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} - - run: bash ./scripts/test-pnp.sh + - uses: actions/download-artifact@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native - testsPass: - name: thank you, next - runs-on: ubuntu-latest - needs: [lint, checkPrecompiled, testIntegration, testUnit, testYarnPnP] - steps: - - run: exit 0 + - run: npm i -g playwright-chromium@1.14.1 && npx playwright install-deps + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - run: xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/6 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - name: Upload test trace + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-trace + if-no-files-found: ignore + retention-days: 2 + path: | + test/traces - testLegacyWebpack: - name: Webpack 4 (Basic, Production, Acceptance) + testElectron: + name: Test Electron runs-on: ubuntu-latest - needs: build + needs: [build, build-native-dev] env: NEXT_TELEMETRY_DISABLED: 1 NEXT_TEST_JOB: 1 - HEADLESS: true - NEXT_PRIVATE_TEST_WEBPACK4_MODE: 1 - + TEST_ELECTRON: 1 steps: - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 - uses: actions/cache@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + + - run: cd test/integration/with-electron/app && yarn + if: ${{needs.build.outputs.docsChange != 'docs only change'}} - - run: xvfb-run node run-tests.js test/integration/{basic,fallback-modules,link-ref,production,async-modules,font-optimization,ssr-ctx}/test/index.test.js test/acceptance/*.test.js + - run: xvfb-run node run-tests.js test/integration/with-electron/test/index.test.js if: ${{needs.build.outputs.docsChange != 'docs only change'}} + testsPass: + name: thank you, next + runs-on: ubuntu-latest + needs: + [ + lint, + check-examples, + test-native, + checkPrecompiled, + testIntegration, + testUnit, + testDev, + testProd, + ] + steps: + - run: exit 0 + testFirefox: name: Test Firefox (production) runs-on: ubuntu-latest - needs: build + needs: [build, build-native-dev] env: - HEADLESS: true BROWSER_NAME: 'firefox' NEXT_TELEMETRY_DISABLED: 1 steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + - uses: actions/cache@v2 if: ${{needs.build.outputs.docsChange != 'docs only change'}} id: restore-build with: path: ./* - key: ${{ github.sha }} - - run: node run-tests.js -c 1 test/integration/production/test/index.test.js + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + - run: npx playwright install-deps && npx playwright install firefox + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + - run: node run-tests.js test/integration/production/test/index.test.js if: ${{needs.build.outputs.docsChange != 'docs only change'}} testSafari: name: Test Safari (production) runs-on: ubuntu-latest - needs: build + needs: [build, build-native-dev] env: BROWSERSTACK: true BROWSER_NAME: 'safari' NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_MODE: 'start' SKIP_LOCAL_SELENIUM_SERVER: true BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + # https://github.com/actions/virtual-environments/issues/1187 - name: tune linux network run: sudo ethtool -K eth0 tx off rx off @@ -255,14 +422,26 @@ jobs: id: restore-build with: path: ./* - key: ${{ github.sha }} - - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js -c 1 test/integration/production/test/index.test.js' + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + + # TODO: use macos runner so that we can use playwright to test against + # PRs instead of only running on canary? + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || npm i -g browserstack-local@1.4.0' + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js -c 1 test/integration/production/test/index.test.js test/e2e/basepath.test.ts' if: ${{needs.build.outputs.docsChange != 'docs only change'}} testSafariOld: name: Test Safari 10.1 (nav) runs-on: ubuntu-latest - needs: [build, testSafari] + needs: [build, testSafari, build-native-dev] env: BROWSERSTACK: true LEGACY_SAFARI: true @@ -272,6 +451,12 @@ jobs: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + # https://github.com/actions/virtual-environments/issues/1187 - name: tune linux network run: sudo ethtool -K eth0 tx off rx off @@ -281,18 +466,74 @@ jobs: id: restore-build with: path: ./* - key: ${{ github.sha }} - - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js -c 1 test/integration/production-nav/test/index.test.js' + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || npm i -g browserstack-local@1.4.0' + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production-nav/test/index.test.js' + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + testFirefoxNode17: + name: Test Firefox Node.js 17 + runs-on: ubuntu-latest + needs: [build, testFirefox, build-native-dev] + env: + BROWSER_NAME: 'firefox' + NEXT_TELEMETRY_DISABLED: 1 + steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 17 + check-latest: true + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + - run: npx playwright install-deps && npx playwright install firefox + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + - run: node run-tests.js test/integration/production/test/index.test.js if: ${{needs.build.outputs.docsChange != 'docs only change'}} publishRelease: - if: ${{ startsWith(github.ref, 'refs/tags/v') }} + if: ${{ needs.build.outputs.isRelease == 'true' }} name: Potentially publish release runs-on: ubuntu-latest - needs: [build, build-native] + needs: + - build + - build-wasm + - build-native + - build-windows-i686 + - build-windows-aarch64 + - build-linux-musl + - build-linux-arm7 + - build-linux-aarch64-gnu + - build-android-aarch64 + - build-linux-aarch64-musl env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + # https://github.com/actions/virtual-environments/issues/1187 - name: tune linux network run: sudo ethtool -K eth0 tx off rx off @@ -301,11 +542,18 @@ jobs: id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + - uses: actions/download-artifact@v2 with: name: next-swc-binaries - path: packages/next/build/swc/dist + path: packages/next-swc/native + + - uses: actions/download-artifact@v2 + with: + name: wasm-binaries + path: packages/next-swc/crates/wasm + - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc - run: ./scripts/publish-native.js $GITHUB_REF - run: ./scripts/publish-release.sh @@ -313,127 +561,813 @@ jobs: releaseStats: name: Release Stats runs-on: ubuntu-latest - needs: [publishRelease] + needs: [publishRelease, build-native-dev] steps: + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.docsChange != 'docs only change' }} + with: + node-version: 14 + - uses: actions/cache@v2 id: restore-build with: path: ./* - key: ${{ github.sha }} + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - uses: actions/download-artifact@v2 + with: + name: next-swc-dev-binary + path: packages/next-swc/native + + - run: cp -r packages/next-swc/native .github/actions/next-stats-action/native + - run: ./scripts/release-stats.sh - uses: ./.github/actions/next-stats-action env: PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} - build-native: - strategy: - matrix: - os: [ubuntu-18.04, macos-latest, windows-latest] - description: [default] - include: - - os: ubuntu-18.04 - target: x86_64-unknown-linux-gnu - - os: windows-latest - target: x86_64-pc-windows-msvc - - os: macos-latest - target: x86_64-apple-darwin - - os: macos-latest - target: aarch64-apple-darwin - description: m1 - - name: next-swc - ${{ matrix.os }} - ${{ matrix.target }} - node@14 - runs-on: ${{ matrix.os }} - + build-native-dev: + name: Build dev binary for tests + runs-on: ubuntu-18.04 steps: # https://github.com/actions/virtual-environments/issues/1187 - name: tune linux network run: sudo ethtool -K eth0 tx off rx off - if: ${{ matrix.os == 'ubuntu-18.04' }} - - name: tune windows network - run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6 - if: ${{ matrix.os == 'windows-latest' }} - - name: tune mac network - run: sudo sysctl -w net.link.generic.system.hwcksum_tx=0 && sudo sysctl -w net.link.generic.system.hwcksum_rx=0 - if: ${{ matrix.os == 'macos-latest' }} - uses: actions/checkout@v2 with: fetch-depth: 25 + - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') id: docs-change + - name: Setup node uses: actions/setup-node@v2 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: node-version: 14 check-latest: true + - name: Install uses: actions-rs/toolchain@v1 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: profile: minimal - toolchain: nightly-2021-03-25 - target: ${{ matrix.target }} + toolchain: nightly-2021-11-15 + - name: Cache cargo registry uses: actions/cache@v1 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: path: ~/.cargo/registry - key: stable-${{ matrix.os }}-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + key: stable-ubuntu-18.04-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index uses: actions/cache@v1 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: path: ~/.cargo/git - key: stable-${{ matrix.os }}-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache native binary - id: binary-cache + key: stable-ubuntu-18.04-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + + # We use week in the turbo cache key to keep the cache from infinitely growing + - id: get-week + run: echo ::set-output name=WEEK::$(date +%U) + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- + turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- + + # We use restore-key to pick latest cache. + # We will not get exact match, but doc says + # "If there are multiple partial matches for a restore key, the action returns the most recently created cache." + # So we get latest cache + - name: Cache built files uses: actions/cache@v2 + with: + path: ./packages/next-swc/target + key: next-swc-cargo-cache-dev-ubuntu-18.04-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + next-swc-cargo-cache-dev-ubuntu-18.04 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + + - name: Build + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: turbo run build-native --cache-dir=".turbo" + env: + MACOSX_DEPLOYMENT_TARGET: '10.13' + + - name: Upload artifact + uses: actions/upload-artifact@v2.2.4 + with: + name: next-swc-dev-binary + path: packages/next-swc/native/next-swc.linux-x64-gnu.node + + - name: Clear the cargo caches if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache + + test-native: + name: Unit Test Native Code + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 25 + - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') + id: docs-change + - name: Install + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2021-11-15 + profile: minimal + - run: cd packages/next-swc && cargo test + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + + test-wasm: + name: Test the wasm build + runs-on: ubuntu-18.04 + needs: [build, build-native-dev, build-wasm-dev] + + steps: + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }} + + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: wasm-dev-binary + path: packages/next-swc/crates/wasm/pkg-nodejs + + - run: ls packages/next-swc/crates/wasm + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - uses: actions/download-artifact@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + + # node version needs to be 16+ to use --no-addons option + - name: Setup node + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + uses: actions/setup-node@v2 + with: + node-version: 16 + check-latest: true + + - run: npm i -g playwright-chromium@1.14.1 && npx playwright install-deps + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - run: node ./scripts/setup-wasm.mjs + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + - run: TEST_WASM=true xvfb-run node run-tests.js test/integration/production/test/index.test.js + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + + # Build binaries for publishing + build-native: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + strategy: + matrix: + os: [ubuntu-18.04, macos-latest, windows-latest] + description: [default] + include: + - os: ubuntu-18.04 + target: x86_64-unknown-linux-gnu + name: linux-x64-gnu + - os: windows-latest + target: x86_64-pc-windows-msvc + name: win32-x64-msvc + - os: macos-latest + target: x86_64-apple-darwin + name: darwin-x64 + - os: macos-latest + target: aarch64-apple-darwin + name: darwin-arm64 + description: m1 + + name: next-swc - ${{ matrix.os }} - ${{ matrix.target }} - node@14 + runs-on: ${{ matrix.os }} + + steps: + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + if: ${{ matrix.os == 'ubuntu-18.04' }} + - name: tune windows network + run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6 + if: ${{ matrix.os == 'windows-latest' }} + - name: tune mac network + run: sudo sysctl -w net.link.generic.system.hwcksum_tx=0 && sudo sysctl -w net.link.generic.system.hwcksum_rx=0 + if: ${{ matrix.os == 'macos-latest' }} + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v2 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + + - name: Install + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-11-15 + target: ${{ matrix.target }} + + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: stable-${{ matrix.os }}-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v1 with: - path: packages/next/native/next-swc.*.node - key: next-swc-nightly-2021-03-25-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_test_deploy.yml', 'packages/next/build/swc/**') }} + path: ~/.cargo/git + key: stable-${{ matrix.os }}-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: Turbo cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ matrix.name }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}-${{ matrix.name }}- + turbo-${{ github.job }}-${{ matrix.name }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-${{ matrix.name }}-canary-${{ needs.build.outputs.weekNum }}- + - name: Cross build aarch64 setup - if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + if: ${{ matrix.target == 'aarch64-apple-darwin' }} run: | sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; export CC=$(xcrun -f clang); export CXX=$(xcrun -f clang++); SYSROOT=$(xcrun --sdk macosx --show-sdk-path); export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; + # We use restore-key to pick latest cache. + # We will not get exact match, but doc says + # "If there are multiple partial matches for a restore key, the action returns the most recently created cache." + # So we get latest cache + - name: Cache built files + uses: actions/cache@v2 + with: + path: ./packages/next-swc/target + key: next-swc-cargo-cache-${{ matrix.os }}--${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + next-swc-cargo-cache-${{ matrix.os }} + - name: 'Build' - if: ${{ steps.binary-cache.outputs.cache-hit != 'true' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} - run: yarn build-native --target ${{ matrix.target }} + shell: bash + run: turbo run build-native --cache-dir=".turbo" -- --release --target ${{ matrix.target }} env: MACOSX_DEPLOYMENT_TARGET: '10.13' - working-directory: packages/next + - name: Upload artifact - uses: actions/upload-artifact@v2 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + uses: actions/upload-artifact@v2.2.4 with: name: next-swc-binaries - path: packages/next/native/next-swc.*.node + path: packages/next-swc/native/next-swc.${{ matrix.name }}.node + - name: Clear the cargo caches - if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} run: | cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache - test-native: - name: Unit Test Native Code + build-windows-i686: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: next-swc - windows-i686 - node@14 + runs-on: windows-latest + env: + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 32 + CARGO_PROFILE_RELEASE_LTO: 'false' + steps: + - name: Install node x86 + run: | + choco install nodejs-lts --x86 -y --force + refreshenv + + - name: Set 32bit Node.js path + run: | + echo "C:\\Program Files (x86)\\nodejs" >> $GITHUB_PATH + shell: bash + + - name: Node.js arch + run: node -e "console.log(process.arch)" + + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v2 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-11-15 + override: true + target: i686-pc-windows-msvc + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Build + shell: bash + run: turbo run build-native --cache-dir=".turbo" -- --release --target i686-pc-windows-msvc + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.win32-ia32-msvc.node + + build-windows-aarch64: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: next-swc - windows-aarch64 - node@14 + runs-on: windows-latest + steps: + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v2 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-11-15 + override: true + target: aarch64-pc-windows-msvc + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Build + shell: bash + run: turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.win32-arm64-msvc.node + + build-linux-musl: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: next-swc - linux-musl - node@lts + runs-on: ubuntu-latest + steps: + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v2 + + - name: Login to registry + run: | + docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD $DOCKER_REGISTRY_URL + env: + DOCKER_REGISTRY_URL: ghcr.io + DOCKER_USERNAME: ${{ github.actor }} + DOCKER_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + + - name: Cache + uses: actions/cache@v2 + with: + path: | + target/ + key: linux-musl-publish-integration + + - name: Pull docker image + run: | + docker pull ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + docker tag ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine builder + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: 'Build' + run: | + docker run --rm -v ~/.cargo/git:/root/.cargo/git -v ~/.cargo/registry:/root/.cargo/registry -v $(pwd):/build -w /build builder sh -c "npm i -g @napi-rs/cli@1.2.1 && npm i -g turbo@1.0.28 && turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl" + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.linux-x64-musl.node + + build-linux-aarch64-gnu: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: next-swc - aarch64-unknown-linux-gnu - node@14 runs-on: ubuntu-18.04 + steps: + - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v2 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-11-15 + override: true + target: aarch64-unknown-linux-gnu + - name: Cache + uses: actions/cache@v2 + with: + path: | + target/ + key: aarch64-linux-gnu-publish-integration + + - name: Install cross compile toolchain + run: | + sudo apt-get update + sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu -y + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Cross build aarch64 + if: ${{ steps.binary-cache.outputs.cache-hit != 'true' }} + run: turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-gnu + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.linux-arm64-gnu.node + + build-linux-aarch64-musl: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: next-swc - aarch64-unknown-linux-musl - node@14 + runs-on: ubuntu-18.04 steps: + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' - uses: actions/checkout@v2 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + - name: Install Rust + uses: actions-rs/toolchain@v1 with: - fetch-depth: 25 - - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') - id: docs-change - - name: Install - if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + profile: minimal + toolchain: nightly-2021-11-15 + override: true + target: aarch64-unknown-linux-musl + + - name: Cache + uses: actions/cache@v2 + with: + path: | + target/ + key: aarch64-linux-musl-publish-integration + + - name: Install cross compile toolchain + run: | + sudo apt-get update + sudo apt-get install gcc-aarch64-linux-gnu -y + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Cross build aarch64 + run: turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.linux-arm64-musl.node + + build-linux-arm7: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: next-swc - arm7-unknown-linux-gnu - node@14 + runs-on: ubuntu-18.04 + steps: + - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v2 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + + - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2021-03-25 profile: minimal - - run: cd packages/next/build/swc && cargo test + toolchain: nightly-2021-11-15 + override: true + target: armv7-unknown-linux-gnueabihf + + - name: Cache + uses: actions/cache@v2 + with: + path: | + target/ + key: arm7-linux-gnu-publish-integration + + - name: Install cross compile toolchain + run: | + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabihf -y + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Cross build aarch64 + run: turbo run build-native --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.linux-arm-gnueabihf.node + + build-android-aarch64: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: next-swc - Android - aarch64 + runs-on: macos-latest + steps: + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v2 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-11-15 + override: true + target: aarch64-linux-android + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Build + shell: bash + run: | + export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang" + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.android-arm64.node + + build-wasm: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + strategy: + matrix: + target: [web, nodejs] + runs-on: ubuntu-latest + steps: + - uses: actions/cache@v2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-11-15 + override: true + target: wasm32-unknown-unknown + + - run: npm i -g turbo@1.0.28 + + - name: Turbo cache + id: turbo-cache + uses: actions/cache@v2 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ matrix.target }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}-${{ matrix.target }}- + turbo-${{ github.job }}-${{ matrix.target }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-${{ matrix.target }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Build + run: turbo run build-wasm --cache-dir=".turbo" -- --target ${{ matrix.target }} + + - name: Add target to folder name + run: '[[ -d "packages/next-swc/crates/wasm/pkg" ]] && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-${{ matrix.target }} || ls packages/next-swc/crates/wasm' + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: wasm-binaries + path: packages/next-swc/crates/wasm/pkg-* + + build-wasm-dev: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/cache@v2 + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - name: Setup node + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + uses: actions/setup-node@v2 + with: + node-version: 14 + + - run: npm i -g turbo@1.0.28 + + - name: Install Rust + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-11-15 + override: true + target: wasm32-unknown-unknown + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- + turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- + + - name: Install wasm-pack + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Build + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + run: turbo run build-wasm --cache-dir=".turbo" -- --target nodejs --dev + + - name: Add target to folder name + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + run: '[[ -d "packages/next-swc/crates/wasm/pkg" ]] && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-nodejs || ls packages/next-swc/crates/wasm' + + - name: Upload artifact + if: ${{needs.build.outputs.docsChange != 'docs only change'}} + uses: actions/upload-artifact@v2 + with: + name: wasm-dev-binary + path: packages/next-swc/crates/wasm/pkg-nodejs diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml index ce58d11cab7c..093016a7beaf 100644 --- a/.github/workflows/cancel.yml +++ b/.github/workflows/cancel.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 2 steps: - - uses: styfle/cancel-workflow-action@0.5.0 + - uses: styfle/cancel-workflow-action@0.9.1 with: workflow_id: 444921, 444987 access_token: ${{ github.token }} diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 000000000000..71a116d17bb6 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,26 @@ +name: 'Lock Threads' + +on: + schedule: + # This runs every hour: https://crontab.guru/every-1-hour + - cron: '0 * * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +concurrency: + group: lock + +jobs: + action: + runs-on: ubuntu-latest + if: github.repository_owner == 'vercel' + steps: + - uses: dessant/lock-threads@v3 + with: + github-token: ${{ secrets.LOCK_TOKEN }} + issue-inactive-days: 30 + issue-comment: 'This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.' + pr-inactive-days: 30 diff --git a/.github/workflows/pull_request_stats.yml b/.github/workflows/pull_request_stats.yml index 4c4874cdb9f4..0d1e32362409 100644 --- a/.github/workflows/pull_request_stats.yml +++ b/.github/workflows/pull_request_stats.yml @@ -5,8 +5,105 @@ on: name: Generate Pull Request Stats jobs: + build-native-dev: + name: Build dev binary for tests + runs-on: ubuntu-18.04 + steps: + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/checkout@v2 + with: + fetch-depth: 25 + + - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') + id: docs-change + + - name: Setup node + uses: actions/setup-node@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + node-version: 14 + check-latest: true + + - name: Install + uses: actions-rs/toolchain@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + profile: minimal + toolchain: nightly-2021-11-15 + + - name: Cache cargo registry + uses: actions/cache@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: ~/.cargo/registry + key: stable-ubuntu-18.04-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: ~/.cargo/git + key: stable-ubuntu-18.04-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + + # We use week in the turbo cache key to keep the cache from infinitely growing + - id: get-week + run: echo ::set-output name=WEEK::$(date +%U) + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- + turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- + + # We use restore-key to pick latest cache. + # We will not get exact match, but doc says + # "If there are multiple partial matches for a restore key, the action returns the most recently created cache." + # So we get latest cache + - name: Cache built files + uses: actions/cache@v2 + with: + path: ./packages/next-target + key: next-swc-cargo-cache-ubuntu-18.04--${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + next-swc-cargo-cache-ubuntu-18.04 + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@1.2.1 + - run: npm i -g turbo@1.0.28 + + - name: Build + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: turbo run build-native --cache-dir=".turbo" + env: + MACOSX_DEPLOYMENT_TARGET: '10.13' + TURBO_TOKEN: ${{secrets.TURBO_TOKEN}} + TURBO_TEAM: nextjs + TURBO_PROJECT: nextjs + + - name: Upload artifact + uses: actions/upload-artifact@v2.2.4 + with: + name: next-swc-dev-binary + path: packages/next-swc/native/next-swc.linux-x64-gnu.node + + - name: Clear the cargo caches + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache + stats: name: PR Stats + needs: build-native-dev runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -15,5 +112,15 @@ jobs: - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change') id: docs-change + + - uses: actions/download-artifact@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + + - run: cp -r packages/next-swc/native .github/actions/next-stats-action/native + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + - uses: ./.github/actions/next-stats-action if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000000..9c858d67608b --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,25 @@ +name: 'Stale issue handler' +on: + workflow_dispatch: + schedule: + # This runs every day 20 minutes before midnight: https://crontab.guru/#40_23_*_*_* + - cron: '40 23 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + if: github.repository_owner == 'vercel' + steps: + - uses: actions/stale@v4 + id: stale + name: 'Close stale issues with no reproduction' + with: + repo-token: ${{ secrets.STALE_TOKEN }} + only-labels: 'please add a complete reproduction' + close-issue-message: 'This issue has been automatically closed after 30 days of inactivity with no reproduction. If you are running into a similar issue, please open a new issue with a reproduction. Thank you.' + days-before-issue-close: 1 + days-before-issue-stale: 30 + days-before-pr-close: -1 + days-before-pr-stale: -1 + exempt-issue-labels: 'blocked,must,should,keep' + operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml deleted file mode 100644 index 573425c04aae..000000000000 --- a/.github/workflows/test_macos.yml +++ /dev/null @@ -1,27 +0,0 @@ -on: - push: - branches: [canary] - paths-ignore: - - 'bench/**' - - 'docs/**' - - 'errors/**' - - 'examples/**' - -name: Test macOS - -jobs: - testMacOS: - name: macOS (Basic, Production, Acceptance) - runs-on: macos-latest - env: - NEXT_TELEMETRY_DISABLED: 1 - NEXT_TEST_JOB: 1 - HEADLESS: true - - steps: - - uses: actions/checkout@v2 - - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - - run: yarn install --frozen-lockfile --check-files || yarn install --frozen-lockfile --check-files - - run: node run-tests.js test/integration/production/test/index.test.js - - run: node run-tests.js test/integration/basic/test/index.test.js - - run: node run-tests.js test/acceptance/* diff --git a/.github/workflows/test_react_experimental.yml b/.github/workflows/test_react_experimental.yml index bbfb890544d6..8b56f79f4d9b 100644 --- a/.github/workflows/test_react_experimental.yml +++ b/.github/workflows/test_react_experimental.yml @@ -6,50 +6,44 @@ on: name: Test react@experimental jobs: - # build: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 - # - run: yarn install --frozen-lockfile --check-files - # env: - # NEXT_TELEMETRY_DISABLED: 1 + - run: yarn install --frozen-lockfile --check-files + env: + NEXT_TELEMETRY_DISABLED: 1 - # - run: yarn upgrade react@next react-dom@next -W --dev + - run: yarn upgrade react@experimental react-dom@experimental -W --dev + + - run: node run-tests.js --timings --write-timings -g 1/1 - # - uses: actions/cache@v2 - # id: cache-build - # with: - # path: ./* - # key: ${{ github.sha }} + - uses: actions/cache@v2 + id: cache-build + with: + path: ./* + key: ${{ github.sha }}-react-experimental testAll: name: Test All runs-on: ubuntu-latest - # needs: build + needs: build env: NEXT_TELEMETRY_DISABLED: 1 - HEADLESS: true - NEXT_PRIVATE_SKIP_SIZE_TESTS: true NEXT_PRIVATE_REACT_ROOT: 1 + NEXT_PRIVATE_SKIP_SIZE_TESTS: true strategy: fail-fast: false matrix: group: [1, 2, 3, 4, 5, 6] steps: - # - uses: actions/cache@v2 - # id: restore-build - # with: - # path: ./* - # key: ${{ github.sha }} - - - uses: actions/checkout@v2 - - - run: yarn install --frozen-lockfile --check-files - - - run: yarn upgrade react@experimental react-dom@experimental -W --dev + - uses: actions/cache@v2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-react-experimental - # TODO: remove after we fix watchpack watching too much - - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + - run: npm i -g playwright-chromium@1.14.1 && npx playwright install-deps - - run: node run-tests.js --timings -g ${{ matrix.group }}/6 -c 3 + - run: node run-tests.js --timings -g ${{ matrix.group }}/6 diff --git a/.github/workflows/test_react_next.yml b/.github/workflows/test_react_next.yml index 4845ca86c9a5..4411c0a521a5 100644 --- a/.github/workflows/test_react_next.yml +++ b/.github/workflows/test_react_next.yml @@ -6,50 +6,44 @@ on: name: Test react@next jobs: - # build: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 - # - run: yarn install --frozen-lockfile --check-files - # env: - # NEXT_TELEMETRY_DISABLED: 1 + - run: yarn install --frozen-lockfile --check-files + env: + NEXT_TELEMETRY_DISABLED: 1 - # - run: yarn upgrade react@next react-dom@next -W --dev + - run: yarn upgrade react@next react-dom@next -W --dev + + - run: node run-tests.js --timings --write-timings -g 1/1 - # - uses: actions/cache@v2 - # id: cache-build - # with: - # path: ./* - # key: ${{ github.sha }} + - uses: actions/cache@v2 + id: cache-build + with: + path: ./* + key: ${{ github.sha }}-react-next testAll: name: Test All runs-on: ubuntu-latest - # needs: build + needs: build env: NEXT_TELEMETRY_DISABLED: 1 - HEADLESS: true - NEXT_PRIVATE_SKIP_SIZE_TESTS: true NEXT_PRIVATE_REACT_ROOT: 1 + NEXT_PRIVATE_SKIP_SIZE_TESTS: true strategy: fail-fast: false matrix: group: [1, 2, 3, 4, 5, 6] steps: - # - uses: actions/cache@v2 - # id: restore-build - # with: - # path: ./* - # key: ${{ github.sha }} - - - uses: actions/checkout@v2 - - - run: yarn install --frozen-lockfile --check-files - - - run: yarn upgrade react@next react-dom@next -W --dev + - uses: actions/cache@v2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-react-next - # TODO: remove after we fix watchpack watching too much - - run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + - run: npm i -g playwright-chromium@1.14.1 && npx playwright install-deps - - run: node run-tests.js --timings -g ${{ matrix.group }}/6 -c 3 + - run: node run-tests.js --timings -g ${{ matrix.group }}/6 diff --git a/.gitignore b/.gitignore index 011b7fffcbbd..a1e1b057fd45 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ test/node_modules # logs & pids *.log pids +*.cpuprofile # coverage .nyc_output @@ -23,6 +24,7 @@ test/**/out* test/**/next-env.d.ts .DS_Store /e2e-tests +test/tmp/** # Editors **/.idea @@ -38,3 +40,6 @@ test-timings.json # Vercel .vercel .now + +# Cache +*.tsbuildinfo diff --git a/.prettierignore b/.prettierignore index 101632425ad1..3f2ee9a47ef8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,6 +3,7 @@ node_modules **/_next/** **/dist/** packages/next/bundles/webpack/packages/*.runtime.js +packages/next/bundles/webpack/packages/lazy-compilation-*.js packages/next/compiled/** packages/react-refresh-utils/**/*.js packages/react-refresh-utils/**/*.d.ts @@ -10,6 +11,7 @@ packages/react-dev-overlay/lib/** **/__tmp__/** lerna.json .github/actions/next-stats-action/.work +packages/next-swc/crates/**/* packages/next-codemod/transforms/__testfixtures__/**/* packages/next-codemod/transforms/__tests__/**/* packages/next-codemod/**/*.js @@ -17,3 +19,5 @@ packages/next-codemod/**/*.d.ts packages/next-env/**/*.d.ts test-timings.json test/**/out/** +bench/nested-deps/pages/**/* +bench/nested-deps/components/**/* diff --git a/.prettierignore_staged b/.prettierignore_staged index e888b2691913..014df0fc5117 100644 --- a/.prettierignore_staged +++ b/.prettierignore_staged @@ -1,6 +1,7 @@ **/.next/** **/_next/** **/dist/** +packages/next-swc/crates/** packages/next/compiled/**/* packages/next/bundles/webpack/packages/*.runtime.js lerna.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 9296ff142e95..8c78fbbb7103 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,10 +10,13 @@ "request": "launch", "cwd": "${workspaceFolder}", "runtimeExecutable": "yarn", - "runtimeArgs": ["run", "debug", "dev", "test/integration/basic"], + "runtimeArgs": ["run", "debug", "dev", "bench/nested-deps"], "skipFiles": ["/**"], "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"], - "port": 9229 + "port": 9229, + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK5": "1" + } }, { "name": "Launch app build", @@ -21,10 +24,13 @@ "request": "launch", "cwd": "${workspaceFolder}", "runtimeExecutable": "yarn", - "runtimeArgs": ["run", "debug", "build", "test/integration/basic"], + "runtimeArgs": ["run", "debug", "build", "bench/nested-deps"], "skipFiles": ["/**"], "port": 9229, - "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] + "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK5": "1" + } }, { "name": "Launch app build trace jaeger", @@ -35,7 +41,10 @@ "runtimeArgs": ["run", "clean-trace-jaeger"], "skipFiles": ["/**"], "port": 9229, - "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] + "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK5": "1" + } }, { "name": "Launch app production", @@ -43,9 +52,12 @@ "request": "launch", "cwd": "${workspaceFolder}", "runtimeExecutable": "yarn", - "runtimeArgs": ["run", "debug", "start", "test/integration/basic"], + "runtimeArgs": ["run", "debug", "start", "bench/nested-deps"], "skipFiles": ["/**"], - "port": 9229 + "port": 9229, + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK5": "1" + } }, { "type": "node", @@ -53,7 +65,10 @@ "name": "Attach to existing debugger", "port": 9229, "skipFiles": ["/**"], - "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"] + "outFiles": ["${workspaceFolder}/packages/next/dist/**/*"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK5": "1" + } }, { "name": "Launch this example", @@ -63,7 +78,10 @@ "runtimeExecutable": "yarn", "runtimeArgs": ["run", "debug", "dev", "${fileDirname}"], "skipFiles": ["/**"], - "port": 9229 + "port": 9229, + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK5": "1" + } } ] } diff --git a/SECURITY.md b/SECURITY.md index 294bff136eef..7ef6aaaaa6d0 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1 +1,9 @@ -Visit https://vercel.com/security to view the disclosure policy. +# Reporting Security Issues + +If you believe you have found a security vulnerability in Next.js, we encourage you to let us know right away. + +We will investigate all legitimate reports and do our best to quickly fix the problem. + +Email `security@vercel.com` to disclose any security vulnerabilities. + +https://vercel.com/security diff --git a/azure-pipelines.yml b/azure-pipelines.yml index dc023d97813b..57978a2058b6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,14 +10,14 @@ trigger: - docs - errors - examples - # Do not run Azure on `canary`, `master`, or release tags. This unnecessarily + # Do not run Azure on `canary`, `main`, or release tags. This unnecessarily # increases the backlog, and the change was already tested on the PR. branches: include: - '*' exclude: - canary - - master + - main - refs/tags/* pr: @@ -34,7 +34,7 @@ pr: variables: YARN_CACHE_FOLDER: $(Pipeline.Workspace)/.yarn NEXT_TELEMETRY_DISABLED: '1' - node_version: ^12.0.0 + node_version: ^12.22.0 stages: - stage: Build @@ -69,11 +69,9 @@ stages: - stage: Test dependsOn: Build jobs: - - job: test_ie11 + - job: test_integration pool: vmImage: 'windows-2019' - variables: - BROWSER_NAME: internet explorer steps: - checkout: none - task: NodeTool@0 @@ -87,6 +85,10 @@ stages: key: $(Build.SourceVersion) path: $(System.DefaultWorkingDirectory) displayName: Cache Build + + - script: | + npx playwright install chromium + - script: | node run-tests.js -c 1 test/integration/production/test/index.test.js test/integration/css-client-nav/test/index.test.js test/integration/rewrites-has-condition/test/index.test.js displayName: 'Run tests' diff --git a/bench/capture-trace.js b/bench/capture-trace.js deleted file mode 100644 index d7419745c3a0..000000000000 --- a/bench/capture-trace.js +++ /dev/null @@ -1,66 +0,0 @@ -import { createServer } from 'http' -import { writeFileSync } from 'fs' - -const PORT = 9411 -const HOST = '0.0.0.0' - -const traces = [] - -const onReady = () => console.log(`Listening on http://${HOST}:${PORT}`) -const onRequest = async (req, res) => { - if ( - req.method !== 'POST' || - req.url !== '/api/v2/spans' || - (req.headers && req.headers['content-type']) !== 'application/json' - ) { - res.writeHead(200) - return res.end() - } - - try { - const body = JSON.parse(await getBody(req)) - for (const traceEvent of body) { - traces.push(traceEvent) - } - res.writeHead(200) - } catch (err) { - console.warn(err) - res.writeHead(500) - } - - res.end() -} - -const getBody = (req) => - new Promise((resolve, reject) => { - let data = '' - req.on('data', (chunk) => { - data += chunk - }) - req.on('end', () => { - if (!req.complete) { - return reject('Connection terminated before body was received.') - } - resolve(data) - }) - req.on('aborted', () => reject('Connection aborted.')) - req.on('error', () => reject('Connection error.')) - }) - -const main = () => { - const args = process.argv.slice(2) - const outFile = args[0] || `./trace-${Date.now()}.json` - - process.on('SIGINT', () => { - console.log(`\nSaving to ${outFile}...`) - writeFileSync(outFile, JSON.stringify(traces, null, 2)) - process.exit() - }) - - const server = createServer(onRequest) - server.listen(PORT, HOST, onReady) -} - -if (require.main === module) { - main() -} diff --git a/bench/nested-deps/.gitignore b/bench/nested-deps/.gitignore new file mode 100644 index 000000000000..9df615af05da --- /dev/null +++ b/bench/nested-deps/.gitignore @@ -0,0 +1,2 @@ +components/* +CPU* \ No newline at end of file diff --git a/bench/nested-deps/bench.mjs b/bench/nested-deps/bench.mjs new file mode 100644 index 000000000000..d8ad44c23d85 --- /dev/null +++ b/bench/nested-deps/bench.mjs @@ -0,0 +1,199 @@ +import { spawn } from 'child_process' +import fetch from 'node-fetch' +import { + existsSync, + readFileSync, + writeFileSync, + unlinkSync, + promises as fs, +} from 'fs' +import treeKill from 'tree-kill' + +async function killApp(instance) { + await new Promise((resolve, reject) => { + treeKill(instance.pid, (err) => { + if (err) { + if ( + process.platform === 'win32' && + typeof err.message === 'string' && + (err.message.includes(`no running instance of the task`) || + err.message.includes(`not found`)) + ) { + // Windows throws an error if the process is already stopped + // + // Command failed: taskkill /pid 6924 /T /F + // ERROR: The process with PID 6924 (child process of PID 6736) could not be terminated. + // Reason: There is no running instance of the task. + return resolve() + } + return reject(err) + } + + resolve() + }) + }) +} + +class File { + constructor(path) { + this.path = path + this.originalContent = existsSync(this.path) + ? readFileSync(this.path, 'utf8') + : null + } + + write(content) { + if (!this.originalContent) { + this.originalContent = content + } + writeFileSync(this.path, content, 'utf8') + } + + replace(pattern, newValue) { + const currentContent = readFileSync(this.path, 'utf8') + if (pattern instanceof RegExp) { + if (!pattern.test(currentContent)) { + throw new Error( + `Failed to replace content.\n\nPattern: ${pattern.toString()}\n\nContent: ${currentContent}` + ) + } + } else if (typeof pattern === 'string') { + if (!currentContent.includes(pattern)) { + throw new Error( + `Failed to replace content.\n\nPattern: ${pattern}\n\nContent: ${currentContent}` + ) + } + } else { + throw new Error(`Unknown replacement attempt type: ${pattern}`) + } + + const newContent = currentContent.replace(pattern, newValue) + this.write(newContent) + } + + prepend(line) { + const currentContent = readFileSync(this.path, 'utf8') + this.write(line + '\n' + currentContent) + } + + delete() { + unlinkSync(this.path) + } + + restore() { + this.write(this.originalContent) + } +} + +function runNextCommandDev(argv, opts = {}) { + const nextBin = '../../node_modules/.bin/next' + const cwd = process.cwd() + const env = { + ...process.env, + NODE_ENV: undefined, + __NEXT_TEST_MODE: 'true', + ...opts.env, + } + + const nodeArgs = opts.nodeArgs || [] + return new Promise((resolve, reject) => { + const instance = spawn( + 'node', + [...nodeArgs, '--no-deprecation', nextBin, ...argv], + { + cwd, + env, + } + ) + let didResolve = false + + function handleStdout(data) { + const message = data.toString() + const bootupMarkers = { + dev: /compiled .*successfully/i, + start: /started server/i, + } + if ( + (opts.bootupMarker && opts.bootupMarker.test(message)) || + bootupMarkers[opts.nextStart ? 'start' : 'dev'].test(message) + ) { + if (!didResolve) { + didResolve = true + resolve(instance) + } + } + + if (typeof opts.onStdout === 'function') { + opts.onStdout(message) + } + + if (opts.stdout !== false) { + process.stdout.write(message) + } + } + + function handleStderr(data) { + const message = data.toString() + if (typeof opts.onStderr === 'function') { + opts.onStderr(message) + } + + if (opts.stderr !== false) { + process.stderr.write(message) + } + } + + instance.stdout.on('data', handleStdout) + instance.stderr.on('data', handleStderr) + + instance.on('close', () => { + instance.stdout.removeListener('data', handleStdout) + instance.stderr.removeListener('data', handleStderr) + if (!didResolve) { + didResolve = true + resolve() + } + }) + + instance.on('error', (err) => { + reject(err) + }) + }) +} + +function waitFor(millis) { + return new Promise((resolve) => setTimeout(resolve, millis)) +} + +async function main() { + await fs.rmDir('.next', { recursive: true }) + const file = new File('pages/index.jsx') + try { + const instance = await runNextCommandDev(['dev', '--port', '3000']) + const res = await fetch('http://localhost:3000/') + if (res.status !== 200) { + throw new Error('Fetching / failed') + } + + await waitFor(3000) + + file.prepend('// First edit') + + await waitFor(5000) + + file.prepend('// Second edit') + + await waitFor(5000) + + file.prepend('// Third edit') + + await waitFor(5000) + + await killApp(instance) + await fs.rmDir('.next', { recursive: true }) + } finally { + file.restore() + } +} + +main() diff --git a/bench/nested-deps/fuzzponent.js b/bench/nested-deps/fuzzponent.js new file mode 100755 index 000000000000..4904669441a8 --- /dev/null +++ b/bench/nested-deps/fuzzponent.js @@ -0,0 +1,182 @@ +#!/usr/bin/env node +const path = require('path') +const fs = require('fs') + +const getSequenceGenerator = require('random-seed') +const generate = require('@babel/generator').default +const t = require('@babel/types') + +const MIN_COMPONENT_NAME_LEN = 18 +const MAX_COMPONENT_NAME_LEN = 24 +const MIN_CHILDREN = 4 +const MAX_CHILDREN = 80 + +const arrayUntil = (len) => [...Array(len)].map((_, i) => i) + +const generateFunctionalComponentModule = (componentName, children = []) => { + const body = [ + generateImport('React', 'react'), + ...children.map((childName) => generateImport(childName, `./${childName}`)), + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(componentName), + t.arrowFunctionExpression( + [], + t.parenthesizedExpression( + generateJSXElement( + 'div', + children.map((childName) => generateJSXElement(childName)) + ) + ) + ) + ), + ]), + t.exportDefaultDeclaration(t.identifier(componentName)), + ] + + return t.program(body, [], 'module') +} + +const generateJSXElement = (componentName, children = null) => + t.JSXElement( + t.JSXOpeningElement(t.JSXIdentifier(componentName), [], !children), + children ? t.JSXClosingElement(t.JSXIdentifier(componentName)) : null, + children || [], + !children + ) + +const generateImport = (componentName, requireString) => + t.importDeclaration( + [t.importDefaultSpecifier(t.identifier(componentName))], + t.stringLiteral(requireString) + ) + +const validFirstChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +const validOtherChars = validFirstChars.toLowerCase() +function generateComponentName(seqGenerator, opts) { + const numOtherChars = seqGenerator.intBetween(opts.minLen, opts.maxLen) + const firstChar = validFirstChars[seqGenerator.range(validFirstChars.length)] + const otherChars = arrayUntil(numOtherChars).map( + () => validOtherChars[seqGenerator.range(validOtherChars.length)] + ) + return `${firstChar}${otherChars.join('')}` +} + +function* generateModules(name, remainingDepth, seqGenerator, opts) { + const filename = `${name}.${opts.extension}` + let ast + + if (name === 'index') { + name = 'RootComponent' + } + + if (remainingDepth === 0) { + ast = generateFunctionalComponentModule(name) + } else { + const numChildren = seqGenerator.intBetween(opts.minChild, opts.maxChild) + const children = arrayUntil(numChildren).map(() => + generateComponentName(seqGenerator, opts) + ) + ast = generateFunctionalComponentModule(name, children) + + for (const child of children) { + yield* generateModules(child, remainingDepth - 1, seqGenerator, opts) + } + } + + yield { + filename, + content: generate(ast).code, + } +} + +function generateFuzzponents(outdir, seed, depth, opts) { + const seqGenerator = getSequenceGenerator(seed) + + const filenames = new Set() + for (const { filename, content } of generateModules( + 'index', + depth, + seqGenerator, + opts + )) { + if (filenames.has(filename)) { + throw new Error( + `Seed "${seed}" generates output with filename collisions.` + ) + } else { + filenames.add(filename) + } + const fpath = path.join(outdir, filename) + fs.writeFileSync(fpath, `// ${filename}\n\n${content}`) + } +} + +if (require.main === module) { + const { outdir, seed, depth, ...opts } = require('yargs') + .option('depth', { + alias: 'd', + demandOption: true, + describe: 'component hierarchy depth', + type: 'number', + }) + .option('seed', { + alias: 's', + demandOption: true, + describe: 'prng seed', + type: 'number', + }) + .option('outdir', { + alias: 'o', + demandOption: false, + default: process.cwd(), + describe: 'the directory where components should be written', + type: 'string', + normalize: true, + }) + .option('minLen', { + demandOption: false, + default: MIN_COMPONENT_NAME_LEN, + describe: 'the smallest acceptible component name length', + type: 'number', + }) + .option('maxLen', { + demandOption: false, + default: MAX_COMPONENT_NAME_LEN, + describe: 'the largest acceptible component name length', + type: 'number', + }) + .option('minLen', { + demandOption: false, + default: MIN_COMPONENT_NAME_LEN, + describe: 'the smallest acceptible component name length', + type: 'number', + }) + .option('maxLen', { + demandOption: false, + default: MAX_COMPONENT_NAME_LEN, + describe: 'the largest acceptible component name length', + type: 'number', + }) + .option('minChild', { + demandOption: false, + default: MIN_CHILDREN, + describe: 'the smallest number of acceptible component children', + type: 'number', + }) + .option('maxChild', { + demandOption: false, + default: MAX_CHILDREN, + describe: 'the largest number of acceptible component children', + type: 'number', + }) + .option('extension', { + default: 'jsx', + describe: 'extension to use for generated components', + type: 'string', + }).argv + + generateFuzzponents(outdir, seed, depth, opts) +} + +module.exports = generateFuzzponents diff --git a/bench/nested-deps/next.config.js b/bench/nested-deps/next.config.js new file mode 100644 index 000000000000..004e6c18198b --- /dev/null +++ b/bench/nested-deps/next.config.js @@ -0,0 +1,9 @@ +const idx = process.execArgv.indexOf('--cpu-prof') +if (idx >= 0) process.execArgv.splice(idx, 1) + +module.exports = { + eslint: { + ignoreDuringBuilds: true, + }, + swcMinify: true, +} diff --git a/bench/nested-deps/package.json b/bench/nested-deps/package.json new file mode 100644 index 000000000000..ad48206bed2d --- /dev/null +++ b/bench/nested-deps/package.json @@ -0,0 +1,11 @@ +{ + "scripts": { + "prepare": "rimraf components && mkdir components && node ./fuzzponent.js -d 2 -s 206 -o components", + "dev": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node ../../node_modules/next/dist/bin/next dev", + "build": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node ../../node_modules/next/dist/bin/next build", + "start": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node ../../node_modules/next/dist/bin/next start", + "dev-nocache": "rimraf .next && yarn dev", + "dev-cpuprofile-nocache": "rimraf .next && cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node --cpu-prof ../../node_modules/next/dist/bin/next", + "build-nocache": "rimraf .next && yarn build" + } +} diff --git a/bench/nested-deps/pages/index.jsx b/bench/nested-deps/pages/index.jsx new file mode 100644 index 000000000000..f199ad545abf --- /dev/null +++ b/bench/nested-deps/pages/index.jsx @@ -0,0 +1,5 @@ +import Comp from '../components/index.jsx' + +export default function Home() { + return +} diff --git a/bench/package.json b/bench/rendering/package.json similarity index 100% rename from bench/package.json rename to bench/rendering/package.json diff --git a/bench/pages/stateless-big.js b/bench/rendering/pages/stateless-big.js similarity index 100% rename from bench/pages/stateless-big.js rename to bench/rendering/pages/stateless-big.js diff --git a/bench/pages/stateless.js b/bench/rendering/pages/stateless.js similarity index 100% rename from bench/pages/stateless.js rename to bench/rendering/pages/stateless.js diff --git a/bench/readme.md b/bench/rendering/readme.md similarity index 100% rename from bench/readme.md rename to bench/rendering/readme.md diff --git a/contributing.md b/contributing.md index cbb3eddaa9f7..8b9a5f99613e 100644 --- a/contributing.md +++ b/contributing.md @@ -4,19 +4,19 @@ Read about our [Commitment to Open Source](https://vercel.com/oss). To contribute to [our examples](examples), please see **[Adding examples](#adding-examples)** below. +Before jumping into a PR be sure to search [existing PRs](https://github.com/vercel/next.js/pulls) or [issues](https://github.com/vercel/next.js/issues) for an open or closed item that relates to your submission. + ## Developing -The development branch is `canary`, and this is the branch that all pull -requests should be made against. After publishing a stable release, the changes -in the `canary` branch are rebased into `master`. The changes on the `canary` -branch are published to the `@canary` dist-tag daily. +The development branch is `canary`. This is the branch that all pull +requests should be made against. The changes on the `canary` +branch are published to the `@canary` tag on npm regularly. To develop locally: 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then - [clone](https://help.github.com/articles/cloning-a-repository/) it to your - local device. + [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. 2. Create a new branch: ``` git checkout -b MY_BRANCH_NAME @@ -52,20 +52,13 @@ yarn build yarn prepublish ``` +By default the latest canary of the next-swc binaries will be installed and used. If you are actively working on Rust code or you need to test out the most recent Rust code that hasn't been published as a canary yet you can [install Rust](https://www.rust-lang.org/tools/install) and run `yarn --cwd packages/next-swc build-native`. + If you need to clean the project for any reason, use `yarn clean`. ## Testing -Make sure you have `chromedriver` installed, and it should match your Chrome version. -You can install it with: - -- `apt install chromedriver` on Ubuntu/Debian -- `brew install --cask chromedriver` on Mac OS X -- `chocolatey install chromedriver` on Windows - -- Or manually download the version that matches your installed chrome version (if there's no match, download a version under it, but not above) from the [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) and add the binary to `/node_modules/.bin` - -You may also have to [install Rust](https://www.rust-lang.org/tools/install) and build our native packages to see all tests pass locally. We check in binaries for the most common targets and those required for CI so that most people don't have to, but if you do not see a binary for your target in `packages/next/native`, you can build it by running `yarn --cwd packages/next build-native`. If you are working on the Rust code and you need to build the binaries for ci, you can manually trigger [the workflow](https://github.com/vercel/next.js/actions/workflows/build_native.yml) to build and commit with the "Run workflow" button. +See the [testing readme](./test/readme.md) for information on writing tests. ### Running tests @@ -79,25 +72,33 @@ If you would like to run the tests in headless mode (with the browser windows hi yarn testheadless ``` -If you would like to use a specific Chrome/Chromium binary to run tests you can specify it with +Running a specific test suite (e.g. `production`) inside of the `test/integration` directory: ```sh -CHROME_BIN='path/to/chrome/bin' yarn testonly +yarn testonly --testPathPattern "production" ``` -Running a specific test suite inside of the `test/integration` directory: +Running one test in the `production` test suite: ```sh -yarn testonly --testPathPattern "production" +yarn testonly --testPathPattern "production" -t "should allow etag header support" ``` -Running one test in the `production` test suite: +### Linting + +To check the formatting of your code: ```sh -yarn testonly --testPathPattern "production" -t "should allow etag header support" +yarn lint ``` -### Running the integration apps +If you get errors, you can fix them with: + +```sh +yarn lint-fix +``` + +### Running the example apps Running examples can be done with: @@ -126,7 +127,7 @@ EXAMPLE=./test/integration/basic There are two options to develop with your local version of the codebase: -### Set as local dependency in package.json +### Set as a local dependency in package.json 1. In your app's `package.json`, replace: @@ -164,7 +165,24 @@ There are two options to develop with your local version of the codebase: yarn install --force ``` -or +#### Troubleshooting + +- If you see the below error while running `yarn dev` with next: + +``` +Failed to load SWC binary, see more info here: https://nextjs.org/docs/messages/failed-loading-swc +``` + +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" +}, +``` ### Develop inside the monorepo @@ -174,7 +192,7 @@ or This will use the version of `next` built inside of the Next.js monorepo and the main `yarn dev` monorepo command can be running to make changes to the local -Next.js version at the same time (some changes might require re-running `yarn next-with-deps` to take affect). +Next.js version at the same time (some changes might require re-running `yarn next-with-deps` to take effect). ## Adding warning/error descriptions @@ -182,19 +200,12 @@ In Next.js we have a system to add helpful links to warnings and errors. This allows for the logged message to be short while giving a broader description and instructions on how to solve the warning/error. -In general all warnings and errors added should have these links attached. +In general, all warnings and errors added should have these links attached. Below are the steps to add a new link: -1. Create a new markdown file under the `errors` directory based on - `errors/template.md`: - - ```shell - cp errors/template.md errors/.md - ``` - -2. Add the newly added file to `errors/manifest.json` -3. Add the following url to your warning/error: +1. Run `yarn new-error` which will create the error document and update the manifest automatically. +2. Add the following url to your warning/error: `https://nextjs.org/docs/messages/`. For example, to link to `errors/api-routes-static-export.md` you use the url: @@ -206,22 +217,19 @@ When you add an example to the [examples](examples) directory, don’t forget to - Replace `DIRECTORY_NAME` with the directory name you’re adding. - Fill in `Example Name` and `Description`. +- Examples should be TypeScript first, if possible. +- You don’t need to add `name` or `version` in your `package.json`. +- Ensure all your dependencies are up to date. +- Ensure you’re using [`next/image`](https://nextjs.org/docs/api-reference/next/image). - To add additional installation instructions, please add it where appropriate. - To add additional notes, add `## Notes` section at the end. - Remove the `Deploy your own` section if your example can’t be immediately deployed to Vercel. -- Remove the `Preview` section if the example doesn't work on [StackBlitz](http://stackblitz.com/) and file an issue [here](https://github.com/stackblitz/webcontainer-core). ````markdown # Example Name Description -## Preview - -Preview the example live on [StackBlitz](http://stackblitz.com/): - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/DIRECTORY_NAME) - ## Deploy your own Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): diff --git a/docs/advanced-features/automatic-static-optimization.md b/docs/advanced-features/automatic-static-optimization.md index 17a3ea787c3b..8780102d7a67 100644 --- a/docs/advanced-features/automatic-static-optimization.md +++ b/docs/advanced-features/automatic-static-optimization.md @@ -20,7 +20,15 @@ If the above is not the case, Next.js will **statically optimize** your page aut During prerendering, the router's `query` object will be empty since we do not have `query` information to provide during this phase. After hydration, Next.js will trigger an update to your application to provide the route parameters in the `query` object. -> **Note:** Parameters added with [dynamic routes](/docs/routing/dynamic-routes.md) to a page that's using [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) will always be available inside the `query` object. +The cases where the query will be updated after hydration triggering another render are: + +- The page is a [dynamic-route](/docs/routing/dynamic-routes.md). +- The page has query values in the URL. +- [Rewrites](/docs/api-reference/next.config.js/rewrites.md) are configured in your `next.config.js` since these can have parameters that may need to be parsed and provided in the `query`. + +To be able to distinguish if the query is fully updated and ready for use, you can leverage the `isReady` field on [`next/router`](/docs/api-reference/next/router.md#router-object). + +> **Note:** Parameters added with [dynamic routes](/docs/routing/dynamic-routes.md) to a page that's using [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) will always be available inside the `query` object. `next build` will emit `.html` files for statically optimized pages. For example, the result for the page `pages/about.js` would be: @@ -36,5 +44,5 @@ And if you add `getServerSideProps` to the page, it will then be JavaScript, lik ## Caveats -- If you have a [custom `App`](/docs/advanced-features/custom-app.md) with `getInitialProps` then this optimization will be turned off in pages without [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). +- If you have a [custom `App`](/docs/advanced-features/custom-app.md) with `getInitialProps` then this optimization will be turned off in pages without [Static Generation](/docs/basic-features/data-fetching/get-static-props.md). - If you have a [custom `Document`](/docs/advanced-features/custom-document.md) with `getInitialProps` be sure you check if `ctx.req` is defined before assuming the page is server-side rendered. `ctx.req` will be `undefined` for pages that are prerendered. diff --git a/docs/advanced-features/compiler.md b/docs/advanced-features/compiler.md new file mode 100644 index 000000000000..439474fa11f3 --- /dev/null +++ b/docs/advanced-features/compiler.md @@ -0,0 +1,210 @@ +--- +description: Learn about the Next.js Compiler, written in Rust, which transforms and minifies your Next.js application. +--- + +# Next.js Compiler + +
+ Version History + +| Version | Changes | +| --------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `v12.1.0` | Added support for Styled Components, Jest, Relay, Remove React Properties, Legacy Decorators, Remove Console, and jsxImportSource. | +| `v12.0.0` | Next.js Compiler [introduced](https://nextjs.org/blog/next-12). | + +
+ +The Next.js Compiler, written in Rust using [SWC](http://swc.rs/), allows Next.js to transform and minify your JavaScript code for production. This replaces Babel for individual files and Terser for minifying output bundles. + +Compilation using the Next.js Compiler is 17x faster than Babel and enabled by default since Next.js version 12. If you have an existing Babel configuration or are using [unsupported features](#unsupported-features), your application will opt-out of the Next.js Compiler and continue using Babel. + +## Why SWC? + +[SWC](http://swc.rs/) is an extensible Rust-based platform for the next generation of fast developer tools. + +SWC can be used for compilation, minification, bundling, and more – and is designed to be extended. It's something you can call to perform code transformations (either built-in or custom). Running those transformations happens through higher-level tools like Next.js. + +We chose to build on SWC for a few reasons: + +- **Extensibility:** SWC can be used as a Crate inside Next.js, without having to fork the library or workaround design constraints. +- **Performance:** We were able to achieve ~3x faster Fast Refresh and ~5x faster builds in Next.js by switching to SWC, with more room for optimization still in progress. +- **WebAssembly:** Rust's support for WASM is essential for supporting all possible platforms and taking Next.js development everywhere. +- **Community:** The Rust community and ecosystem are amazing and still growing. + +## Supported Features + +### Styled Components + +We're working to port `babel-plugin-styled-components` to the Next.js Compiler. + +First, update to the latest version of Next.js: `npm install next@latest`. Then, update your `next.config.js` file: + +```js +// next.config.js + +module.exports = { + compiler: { + // ssr and displayName are configured by default + styledComponents: true, + }, +} +``` + +Currently, only the `ssr` and `displayName` transforms have been implemented. These two transforms are the main requirement for using `styled-components` in Next.js. + +### Jest + +Jest support not only includes the transformation previously provided by Babel, but also simplifies configuring Jest together with Next.js including: + +- Auto mocking of `.css`, `.module.css` (and their `.scss` variants), and image imports +- Automatically sets up `transform` using SWC +- Loading `.env` (and all variants) into `process.env` +- Ignores `node_modules` from test resolving and transforms +- Ignoring `.next` from test resolving +- Loads `next.config.js` for flags that enable experimental SWC transforms + +First, update to the latest version of Next.js: `npm install next@latest`. Then, update your `jest.config.js` file: + +```js +// jest.config.js +const nextJest = require('next/jest') + +// Providing the path to your Next.js app which will enable loading next.config.js and .env files +const createJestConfig = nextJest({ dir }) + +// Any custom config you want to pass to Jest +const customJestConfig = { + setupFilesAfterEnv: ['/jest.setup.js'], +} + +// createJestConfig is exported in this way to ensure that next/jest can load the Next.js configuration, which is async +module.exports = createJestConfig(customJestConfig) +``` + +### Relay + +To enable [Relay](https://relay.dev/) support: + +```js +// next.config.js +module.exports = { + compiler: { + relay: { + // This should match relay.config.js + src: './', + artifactDirectory: './__generated__', + language: 'typescript', + }, + }, +} +``` + +NOTE: In Next.js all JavaScript files in `pages` directory are considered routes. So, for `relay-compiler` you'll need to specify `artifactDirectory` configuration settings outside of the `pages`, otherwise `relay-compiler` will generate files next to the source file in the `__generated__` directory, and this file will be considered a route, which will break production builds. + +### Remove React Properties + +Allows to remove JSX properties. This is often used for testing. Similar to `babel-plugin-react-remove-properties`. + +To remove properties matching the default regex `^data-test`: + +```js +// next.config.js +module.exports = { + compiler: { + reactRemoveProperties: true, + }, +} +``` + +To remove custom properties: + +```js +// next.config.js +module.exports = { + compiler: { + // The regexes defined here are processed in Rust so the syntax is different from + // JavaScript `RegExp`s. See https://docs.rs/regex. + reactRemoveProperties: { properties: ['^data-custom$'] }, + }, +} +``` + +### Remove Console + +This transform allows for removing all `console.*` calls in application code (not `node_modules`). Similar to `babel-plugin-transform-remove-console`. + +Remove all `console.*` calls: + +```js +// next.config.js +module.exports = { + compiler: { + removeConsole: true, + }, +} +``` + +Remove `console.*` output except `console.error`: + +```js +// next.config.js +module.exports = { + compiler: { + removeConsole: { + exclude: ['error'], + }, + }, +} +``` + +### Legacy Decorators + +Next.js will automatically detect `experimentalDecorators` in `jsconfig.json` or `tsconfig.json`. Legacy decorators are commonly used with older versions of libraries like `mobx`. + +This flag is only supported for compatibility with existing applications. We do not recommend using legacy decorators in new applications. + +First, update to the latest version of Next.js: `npm install next@latest`. Then, update your `jsconfig.json` or `tsconfig.json` file: + +```js +{ + "compilerOptions": { + "experimentalDecorators": true + } +} +``` + +### importSource + +Next.js will automatically detect `jsxImportSource` in `jsconfig.json` or `tsconfig.json` and apply that. This is commonly used with libraries like Theme UI. + +First, update to the latest version of Next.js: `npm install next@latest`. Then, update your `jsconfig.json` or `tsconfig.json` file: + +```js +{ + "compilerOptions": { + "jsxImportSource": 'preact' + } +} +``` + +## Experimental Features + +### Minification + +You can opt-in to using the Next.js compiler for minification. This is 7x faster than Terser. + +```js +// next.config.js + +module.exports = { + swcMinify: true, +} +``` + +If you have feedback about `swcMinify`, please share it on the [feedback discussion](https://github.com/vercel/next.js/discussions/30237). + +## Unsupported Features + +When your application has a `.babelrc` file, Next.js will automatically fall back to using Babel for transforming individual files. This ensures backwards compatibility with existing applications that leverage custom Babel plugins. + +If you're using a custom Babel setup, [please share your configuration](https://github.com/vercel/next.js/discussions/30174). We're working to port as many commonly used Babel transformations as possible, as well as supporting plugins in the future. diff --git a/docs/advanced-features/custom-app.md b/docs/advanced-features/custom-app.md index 71dd20af3257..cf8a8fd1fee3 100644 --- a/docs/advanced-features/custom-app.md +++ b/docs/advanced-features/custom-app.md @@ -38,14 +38,14 @@ export default MyApp The `Component` prop is the active `page`, so whenever you navigate between routes, `Component` will change to the new `page`. Therefore, any props you send to `Component` will be received by the `page`. -`pageProps` is an object with the initial props that were preloaded for your page by one of our [data fetching methods](/docs/basic-features/data-fetching.md), otherwise it's an empty object. +`pageProps` is an object with the initial props that were preloaded for your page by one of our [data fetching methods](/docs/basic-features/data-fetching/overview.md), otherwise it's an empty object. ### Caveats - If your app is running and you added a custom `App`, you'll need to restart the development server. Only required if `pages/_app.js` didn't exist before. -- Adding a custom `getInitialProps` in your `App` will disable [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) in pages without [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). +- Adding a custom [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md) in your `App` will disable [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) in pages without [Static Generation](/docs/basic-features/data-fetching/get-static-props.md). - When you add `getInitialProps` in your custom app, you must `import App from "next/app"`, call `App.getInitialProps(appContext)` inside `getInitialProps` and merge the returned object into the return value. -- `App` currently does not support Next.js [Data Fetching methods](/docs/basic-features/data-fetching.md) like [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering). +- `App` currently does not support Next.js [Data Fetching methods](/docs/basic-features/data-fetching/overview.md) like [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) or [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md). ### TypeScript diff --git a/docs/advanced-features/custom-document.md b/docs/advanced-features/custom-document.md index 2c75cca86b37..843f5db272c8 100644 --- a/docs/advanced-features/custom-document.md +++ b/docs/advanced-features/custom-document.md @@ -4,76 +4,67 @@ description: Extend the default document markup added by Next.js. # Custom `Document` -A custom `Document` is commonly used to augment your application's `` and `` tags. This is necessary because Next.js pages skip the definition of the surrounding document's markup. +A custom `Document` can update the `` and `` tags used to render a [Page](/docs/basic-features/pages.md). This file is only rendered on the server, so event handlers like `onClick` cannot be used in `_document`. -To override the default `Document`, create the file `./pages/_document.js` and extend the `Document` class as shown below: +To override the default `Document`, create the file `pages/_document.js` as shown below: ```jsx -import Document, { Html, Head, Main, NextScript } from 'next/document' - -class MyDocument extends Document { - static async getInitialProps(ctx) { - const initialProps = await Document.getInitialProps(ctx) - return { ...initialProps } - } - - render() { - return ( - - - -
- - - - ) - } +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + +
+ + + + ) } - -export default MyDocument ``` -> The code above is the default `Document` added by Next.js. Feel free to remove the `getInitialProps` or `render` function from `MyDocument` if you don't need to change them. - -``, ``, `
` and `` are required for the page to be properly rendered. - -Custom attributes are allowed as props, like `lang`: +The code above is the default `Document` added by Next.js. Custom attributes are allowed as props. For example, we might want to add `lang="en"` to the `` tag: ```jsx ``` -The `` component used here is not the same one from [`next/head`](/docs/api-reference/next/head.md). The `` component used here should only be used for any `` code that is common for all pages. For all other cases, such as `` tags, we recommend using [`next/head`](/docs/api-reference/next/head.md) in your pages or components. +Or add a `className` to the `body` tag: -The `ctx` object is equivalent to the one received in [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md#context-object), with one addition: +```jsx +<body className="bg-white"> +``` -- `renderPage`: `Function` - a callback that runs the actual React rendering logic (synchronously). It's useful to decorate this function in order to support server-rendering wrappers like Aphrodite's [`renderStatic`](https://github.com/Khan/aphrodite#server-side-rendering) +`<Html>`, `<Head />`, `<Main />` and `<NextScript />` are required for the page to be properly rendered. ## Caveats -- `Document` is only rendered in the server, event handlers like `onClick` won't work. -- React components outside of `<Main />` will not be initialized by the browser. Do _not_ add application logic here or custom CSS (like `styled-jsx`). If you need shared components in all your pages (like a menu or a toolbar), take a look at the [`App`](/docs/advanced-features/custom-app.md) component instead. -- `Document`'s `getInitialProps` function is not called during client-side transitions, nor when a page is [statically optimized](/docs/advanced-features/automatic-static-optimization.md). -- `Document` currently does not support Next.js [Data Fetching methods](/docs/basic-features/data-fetching.md) like [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering). +- The `<Head />` component used in `_document` is not the same as [`next/head`](/docs/api-reference/next/head.md). The `<Head />` component used here should only be used for any `<head>` code that is common for all pages. For all other cases, such as `<title>` tags, we recommend using [`next/head`](/docs/api-reference/next/head.md) in your pages or components. +- React components outside of `<Main />` will not be initialized by the browser. Do _not_ add application logic here or custom CSS (like `styled-jsx`). If you need shared components in all your pages (like a menu or a toolbar), read [Layouts](/docs/basic-features/layouts.md) intead. +- `Document` currently does not support Next.js [Data Fetching methods](/docs/basic-features/data-fetching/overview.md) like [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) or [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md). ## Customizing `renderPage` -> It should be noted that the only reason you should be customizing `renderPage` is for usage with **css-in-js** libraries that need to wrap the application to properly work with server-side rendering. +> **Note:** This is advanced and only needed for libraries like CSS-in-JS to support server-side rendering. This is not needed for built-in `styled-jsx` support. -It takes as argument an options object for further customization: +To prepare for [React 18](/docs/advanced-features/react-18.md), we recommend avoiding customizing `getInitiaProps` and `renderPage`, if possible. + +The `ctx` object shown below is equivalent to the one received in [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md#context-object), with the addition of `renderPage`. ```jsx -import Document from 'next/document' +import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document { static async getInitialProps(ctx) { const originalRenderPage = ctx.renderPage + // Run the React rendering logic synchronously ctx.renderPage = () => originalRenderPage({ - // useful for wrapping the whole react tree + // Useful for wrapping the whole react tree enhanceApp: (App) => App, - // useful for wrapping in a per-page basis + // Useful for wrapping in a per-page basis enhanceComponent: (Component) => Component, }) @@ -82,11 +73,25 @@ class MyDocument extends Document { return initialProps } + + render() { + return ( + <Html> + <Head /> + <body> + <Main /> + <NextScript /> + </body> + </Html> + ) + } } export default MyDocument ``` +> **Note**: `getInitialProps` in `_document` is not called during client-side transitions. + ## TypeScript You can use the built-in `DocumentContext` type and change the file name to `./pages/_document.tsx` like so: diff --git a/docs/advanced-features/custom-error-page.md b/docs/advanced-features/custom-error-page.md index 6dfa999892be..e3f4b2ddf79d 100644 --- a/docs/advanced-features/custom-error-page.md +++ b/docs/advanced-features/custom-error-page.md @@ -21,7 +21,7 @@ export default function Custom404() { } ``` -> **Note**: You can use [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) inside this page if you need to fetch data at build time. +> **Note**: You can use [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) inside this page if you need to fetch data at build time. ## 500 Page @@ -38,7 +38,7 @@ export default function Custom500() { } ``` -> **Note**: You can use [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) inside this page if you need to fetch data at build time. +> **Note**: You can use [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) inside this page if you need to fetch data at build time. ### More Advanced Error Page Customizing @@ -94,3 +94,7 @@ export default function Page({ errorCode, stars }) { The `Error` component also takes `title` as a property if you want to pass in a text message along with a `statusCode`. If you have a custom `Error` component be sure to import that one instead. `next/error` exports the default component used by Next.js. + +### Caveats + +- `Error` does not currently support Next.js [Data Fetching methods](/docs/basic-features/data-fetching.md) like [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) or [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md). diff --git a/docs/advanced-features/custom-server.md b/docs/advanced-features/custom-server.md index f6d95c59e7b2..d7cb7bb6b234 100644 --- a/docs/advanced-features/custom-server.md +++ b/docs/advanced-features/custom-server.md @@ -7,7 +7,6 @@ description: Start a Next.js app programmatically using a custom server. <details> <summary><b>Examples</b></summary> <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/custom-server">Basic custom server</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/custom-server-express">Express integration</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/custom-server-hapi">Hapi integration</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/custom-server-koa">Koa integration</a></li> @@ -17,7 +16,7 @@ description: Start a Next.js app programmatically using a custom server. By default, Next.js includes its own server with `next start`. If you have an existing backend, you can still use it with Next.js (this is not a custom server). A custom Next.js server allows you to start a server 100% programmatically in order to use custom server patterns. Most of the time, you will not need this – but it's available for complete customization. -> **Note:** A custom server **can not** be deployed on [Vercel](https://vercel.com/solutions/nextjs). +> **Note:** A custom server **cannot** be deployed on [Vercel](https://vercel.com/solutions/nextjs). > Before deciding to use a custom server, please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like **serverless functions** and **[Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md).** @@ -30,33 +29,42 @@ const { parse } = require('url') const next = require('next') const dev = process.env.NODE_ENV !== 'production' -const app = next({ dev }) +const hostname = 'localhost' +const port = 3000 +// when using middleware `hostname` and `port` must be provided below +const app = next({ dev, hostname, port }) const handle = app.getRequestHandler() app.prepare().then(() => { - createServer((req, res) => { - // Be sure to pass `true` as the second argument to `url.parse`. - // This tells it to parse the query portion of the URL. - const parsedUrl = parse(req.url, true) - const { pathname, query } = parsedUrl - - if (pathname === '/a') { - app.render(req, res, '/a', query) - } else if (pathname === '/b') { - app.render(req, res, '/b', query) - } else { - handle(req, res, parsedUrl) + createServer(async (req, res) => { + try { + // Be sure to pass `true` as the second argument to `url.parse`. + // This tells it to parse the query portion of the URL. + const parsedUrl = parse(req.url, true) + const { pathname, query } = parsedUrl + + if (pathname === '/a') { + await app.render(req, res, '/a', query) + } else if (pathname === '/b') { + await app.render(req, res, '/b', query) + } else { + await handle(req, res, parsedUrl) + } + } catch (err) { + console.error('Error occurred handling', req.url, err) + res.statusCode = 500 + res.end('internal server error') } - }).listen(3000, (err) => { + }).listen(port, (err) => { if (err) throw err - console.log('> Ready on http://localhost:3000') + console.log(`> Ready on http://${hostname}:${port}`) }) }) ``` > `server.js` doesn't go through babel or webpack. Make sure the syntax and sources this file requires are compatible with the current node version you are running. -Then, to run the custom server you'll need to update the `scripts` in `package.json`, like so: +To run the custom server you'll need to update the `scripts` in `package.json` like so: ```json "scripts": { diff --git a/docs/advanced-features/debugging.md b/docs/advanced-features/debugging.md index d400d334367d..2cbcafec8834 100644 --- a/docs/advanced-features/debugging.md +++ b/docs/advanced-features/debugging.md @@ -4,74 +4,99 @@ description: Debug your Next.js app. # Debugging -This documentation explains how you can debug your Next.js frontend and backend code with full source maps support using either the [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) or the [VSCode debugger](https://code.visualstudio.com/docs/editor/debugging). +This documentation explains how you can debug your Next.js frontend and backend code with full source maps support using either the [VS Code debugger](https://code.visualstudio.com/docs/editor/debugging) or [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools). -It requires you to first launch your Next.js application in debug mode in one terminal and then connect an inspector (Chrome DevTools or VS Code) to it. +Any debugger that can attach to Node.js can also be used to debug a Next.js application. You can find more details in the Node.js [Debugging Guide](https://nodejs.org/en/docs/guides/debugging-getting-started/). -There might be more ways to debug a Next.js application since all it requires is to expose the Node.js debugger and start an inspector client. You can find more details in the [Node.js documentation](https://nodejs.org/en/docs/guides/debugging-getting-started/). +## Debugging with VS Code -## Step 1: Start Next.js in debug mode +Create a file named `.vscode/launch.json` at the root of your project with the following content: -Next.js being a Node.js application, all we have to do is to pass down the [`--inspect`](https://nodejs.org/api/cli.html#cli_node_options_options) flag to the underlying Node.js process for it to start in debug mode. +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: debug server-side", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev" + }, + { + "name": "Next.js: debug client-side", + "type": "pwa-chrome", + "request": "launch", + "url": "http://localhost:3000" + }, + { + "name": "Next.js: debug full stack", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "console": "integratedTerminal", + "serverReadyAction": { + "pattern": "started server on .+, url: (https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } + } + ] +} +``` + +`npm run dev` can be replaced with `yarn dev` if you're using Yarn. If you're [changing the port number](/docs/api-reference/cli#development) your application starts on, replace the `3000` in `http://localhost:3000` with the port you're using instead. + +Now go to the Debug panel (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>D</kbd> on Windows/Linux, <kbd>⇧</kbd>+<kbd>⌘</kbd>+<kbd>D</kbd> on macOS), select a launch configuration, then press <kbd>F5</kbd> or select **Debug: Start Debugging** from the Command Palette to start your debugging session. + +## Debugging with Chrome DevTools + +### Client-side code -First, start Next.js with the inspect flag: +Start your development server as usual by running `next dev`, `npm run dev`, or `yarn dev`. Once the server starts, open `http://localhost:3000` (or your alternate URL) in Chrome. Next, open Chrome's Developer Tools (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>J</kbd> on Windows/Linux, <kbd>⌥</kbd>+<kbd>⌘</kbd>+<kbd>I</kbd> on macOS), then go to the **Sources** tab. + +Now, any time your client-side code reaches a [`debugger`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger) statement, code execution will pause and that file will appear in the debug area. You can also press <kbd>Ctrl</kbd>+<kbd>P</kbd> on Windows/Linux or <kbd>⌘</kbd>+<kbd>P</kbd> on macOS to search for a file and set breakpoints manually. Note that when searching here, your source files will have paths starting with `webpack://_N_E/./`. + +### Server-side code + +To debug server-side Next.js code with Chrome DevTools, you need to pass the [`--inspect`](https://nodejs.org/api/cli.html#cli_inspect_host_port) flag to the underlying Node.js process: ```bash NODE_OPTIONS='--inspect' next dev ``` -If you're using `npm run dev` or `yarn dev` (See: [Getting Started](/docs/getting-started.md)) then you should update the `dev` script on your `package.json`: +If you're using `npm run dev` or `yarn dev` (see [Getting Started](/docs/getting-started)) then you should update the `dev` script on your `package.json`: ```json "dev": "NODE_OPTIONS='--inspect' next dev" ``` -The result of launching Next.js with the inspect flag looks like this: +Launching the Next.js dev server with the `--inspect` flag will look something like this: ```bash Debugger listening on ws://127.0.0.1:9229/0cf90313-350d-4466-a748-cd60f4e47c95 For help, see: https://nodejs.org/en/docs/inspector -ready - started server on http://localhost:3000 +ready - started server on 0.0.0.0:3000, url: http://localhost:3000 ``` -> Be aware that using `NODE_OPTIONS='--inspect' npm run dev` or `NODE_OPTIONS='--inspect' yarn dev` won't work. This would try to start multiple debuggers on the same port: one for the npm/yarn process and one for Next.js. You would then get an error like `Starting inspector on 127.0.0.1:9229 failed: address already in use` in your console. - -## Step 2: Connect to the debugger +> Be aware that running `NODE_OPTIONS='--inspect' npm run dev` or `NODE_OPTIONS='--inspect' yarn dev` won't work. This would try to start multiple debuggers on the same port: one for the npm/yarn process and one for Next.js. You would then get an error like `Starting inspector on 127.0.0.1:9229 failed: address already in use` in your console. -### Using Chrome DevTools +Once the server starts, open a new tab in Chrome and visit `chrome://inspect`, where you should see your Next.js application inside the **Remote Target** section. Click **inspect** under your application to open a separate DevTools window, then go to the **Sources** tab. -Once you open a new tab in Google Chrome and go to `chrome://inspect`, you should see your Next.js application inside the "Remote Target" section. Now click "inspect" to open a screen that will be your debugging environment from now on. +Debugging server-side code here works much like debugging client-side code with Chrome DevTools, except that when you search for files here with <kbd>Ctrl</kbd>+<kbd>P</kbd> or <kbd>⌘</kbd>+<kbd>P</kbd>, your source files will have paths starting with `webpack://{application-name}/./` (where `{application-name}` will be replaced with the name of your application according to your `package.json` file). -### Using the Debugger in Visual Studio Code +### Debugging on Windows -We will be using the [attach mode](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_setting-up-an-attach-configuration) of VS Code to attach the VS Code inspector to our running debugger started in step 1. - -Create a file named `.vscode/launch.json` at the root of your project with this content: +Windows users may run into an issue when using `NODE_OPTIONS='--inspect'` as that syntax is not supported on Windows platforms. To get around this, install the [`cross-env`](https://www.npmjs.com/package/cross-env) package as a development dependency (`--dev` with NPM or `-D` for Yarn) and replace the `dev` script with the following. ```json -{ - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "attach", - "name": "Attach to application", - "skipFiles": ["<node_internals>/**"], - "port": 9229 - } - ] -} +"dev": "cross-env NODE_OPTIONS='--inspect' next dev", ``` -Now hit <kdb>F5</kbd> or select **Debug: Start Debugging** from the Command Palette and you can start your debugging session. - -## Step 3: Put breakpoints and see what happens - -Now you can use the [`debugger`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger) statement to pause your backend or frontend code anytime you want to observe and debug your code more precisely. +`cross-env` will set the `NODE_OPTIONS` environment variable regardless of which platform you are on (including Mac, Linux, and Windows) and allow you to debug consistently across devices and operating systems. -If you trigger the underlying code by refreshing the current page, clicking on a page link or fetching an API route, your code will be paused and the debugger window will appear. +## More information -To learn more on how to use a JavaScript debugger, take a look at the following documentation: +To learn more about how to use a JavaScript debugger, take a look at the following documentation: -- [VS Code Node.js debugging: Breakpoints](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_breakpoints) -- [Get Started with Debugging JavaScript in Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/javascript) +- [Node.js debugging in VS Code: Breakpoints](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_breakpoints) +- [Chrome DevTools: Debug JavaScript](https://developers.google.com/web/tools/chrome-devtools/javascript) diff --git a/docs/advanced-features/i18n-routing.md b/docs/advanced-features/i18n-routing.md index 219a9956e5aa..0a55f52ffd69 100644 --- a/docs/advanced-features/i18n-routing.md +++ b/docs/advanced-features/i18n-routing.md @@ -100,6 +100,8 @@ module.exports = { domains: [ { + // Note: subdomains must be included in the domain value to be matched + // e.g. www.example.com should be used if that is the expected hostname domain: 'example.com', defaultLocale: 'en-US', }, @@ -122,6 +124,7 @@ module.exports = { For example if you have `pages/blog.js` the following urls will be available: - `example.com/blog` +- `www.example.com/blog` - `example.fr/blog` - `example.nl/blog` - `example.nl/nl-BE/blog` @@ -139,6 +142,57 @@ When using Domain Routing, if a user with the `Accept-Language` header `fr;q=0.9 When using Sub-path Routing, the user would be redirected to `/fr`. +### Prefixing the Default Locale + +With Next.js 12 and [Middleware](/docs/middleware.md), we can add a prefix to the default locale with a [workaround](https://github.com/vercel/next.js/discussions/18419). + +For example, here's a `next.config.js` file with support for a few languages. Note the `"default"` locale has been added intentionally. + +```js +// next.config.js + +module.exports = { + i18n: { + locales: ['default', 'en', 'de', 'fr'], + defaultLocale: 'default', + localeDetection: false, + }, + trailingSlash: true, +} +``` + +Next, we can use [Middleware](/docs/middleware.md) to add custom routing rules: + +```js +// pages/_middleware.ts + +import { NextRequest, NextResponse } from 'next/server' + +const PUBLIC_FILE = /\.(.*)$/ + +const stripDefaultLocale = (str: string): string => { + const stripped = str.replace('/default', '') + return stripped +} + +export function middleware(request: NextRequest) { + const shouldHandleLocale = + !PUBLIC_FILE.test(request.nextUrl.pathname) && + !request.nextUrl.pathname.includes('/api/') && + request.nextUrl.locale === 'default' + + return shouldHandleLocale + ? NextResponse.redirect( + `/en${stripDefaultLocale(request.nextUrl.pathname)}${ + request.nextUrl.search + }` + ) + : undefined +} +``` + +This [Middleware](/docs/middleware.md) skips adding the default prefix to [API Routes](/docs/api-routes/introduction.md) and [public](/docs/basic-features/static-file-serving.md) files like fonts or images. If a request is made to the default locale, we redirect to our prefix `/en`. + ### Disabling Automatic Locale Detection The automatic locale detection can be disabled with: @@ -162,7 +216,7 @@ You can access the locale information via the Next.js router. For example, using - `locales` contains all configured locales. - `defaultLocale` contains the configured default locale. -When [pre-rendering](/docs/basic-features/pages.md#static-generation-recommended) pages with `getStaticProps` or `getServerSideProps`, the locale information is provided in [the context](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) provided to the function. +When [pre-rendering](/docs/basic-features/pages.md#static-generation-recommended) pages with `getStaticProps` or `getServerSideProps`, the locale information is provided in [the context](/docs/basic-features/data-fetching/get-static-props.md) provided to the function. When leveraging `getStaticPaths`, the configured locales are provided in the context parameter of the function under `locales` and the configured defaultLocale under `defaultLocale`. @@ -204,6 +258,18 @@ export default function IndexPage(props) { } ``` +Note that to handle switching only the `locale` while preserving all routing information such as [dynamic route](/docs/routing/dynamic-routes.md) query values or hidden href query values, you can provide the `href` parameter as an object: + +```jsx +import { useRouter } from 'next/router' +const router = useRouter() +const { pathname, asPath, query } = router +// change just the locale and maintain all other route information including href's query +router.push({ pathname, query }, asPath, { locale: nextLocale }) +``` + +See [here](/docs/api-reference/next/router.md#with-url-object) for more information on the object structure for `router.push`. + If you have a `href` that already includes the locale you can opt-out of automatically handling the locale prefixing: ```jsx @@ -236,7 +302,7 @@ Next.js doesn't know about variants of a page so it's up to you to add the `href ### Dynamic Routes and `getStaticProps` Pages -For pages using `getStaticProps` with [Dynamic Routes](/docs/routing/dynamic-routes.md), all locale variants of the page desired to be prerendered need to be returned from [`getStaticPaths`](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation). Along with the `params` object returned for `paths`, you can also return a `locale` field specifying which locale you want to render. For example: +For pages using `getStaticProps` with [Dynamic Routes](/docs/routing/dynamic-routes.md), all locale variants of the page desired to be prerendered need to be returned from [`getStaticPaths`](/docs/basic-features/data-fetching/get-static-paths.md). Along with the `params` object returned for `paths`, you can also return a `locale` field specifying which locale you want to render. For example: ```js // pages/blog/[slug].js @@ -256,7 +322,7 @@ For [Automatically Statically Optimized](/docs/advanced-features/automatic-stati For example, if you have 50 locales configured with 10 non-dynamic pages using `getStaticProps`, this means `getStaticProps` will be called 500 times. 50 versions of the 10 pages will be generated during each build. -To decrease the build time of dynamic pages with `getStaticProps`, use a [`fallback` mode](https://nextjs.org/docs/basic-features/data-fetching#fallback-true). This allows you to return only the most popular paths and locales from `getStaticPaths` for prerendering during the build. Then, Next.js will build the remaining pages at runtime as they are requested. +To decrease the build time of dynamic pages with `getStaticProps`, use a [`fallback` mode](/docs/api-reference/data-fetching/get-static-paths#fallback-true). This allows you to return only the most popular paths and locales from `getStaticPaths` for prerendering during the build. Then, Next.js will build the remaining pages at runtime as they are requested. ### Automatically Statically Optimized Pages @@ -294,4 +360,4 @@ export async function getStaticProps({ locale }) { - `locales`: 100 total locales - `domains`: 100 total locale domain items -> **Note:** These limits have been added initially to prevent potential [performance issues at build time](#dynamic-routes-and-getStaticProps-pages). We are continuing to evaluate if these limits are sufficient. +> **Note:** These limits have been added initially to prevent potential [performance issues at build time](#dynamic-routes-and-getStaticProps-pages). You can workaround these limits with custom routing using [Middleware](/docs/middleware.md) in Next.js 12. diff --git a/docs/advanced-features/measuring-performance.md b/docs/advanced-features/measuring-performance.md index 789d94bb8759..0a861b416473 100644 --- a/docs/advanced-features/measuring-performance.md +++ b/docs/advanced-features/measuring-performance.md @@ -164,7 +164,7 @@ export function reportWebVitals(metric) { > ```js > export function reportWebVitals({ id, name, label, value }) { > // Use `window.gtag` if you initialized Google Analytics as this example: -> // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics/pages/_document.js +> // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics/pages/_app.js > window.gtag('event', name, { > event_category: > label === 'web-vital' ? 'Web Vitals' : 'Next.js custom metric', diff --git a/docs/advanced-features/output-file-tracing.md b/docs/advanced-features/output-file-tracing.md new file mode 100644 index 000000000000..844190514753 --- /dev/null +++ b/docs/advanced-features/output-file-tracing.md @@ -0,0 +1,54 @@ +--- +description: Next.js automatically traces which files are needed by each page to allow for easy deployment of your application. Learn how it works here. +--- + +# Output File Tracing + +During a build, Next.js will automatically trace each page and its dependencies to determine all of the files that are needed for deploying a production version of your application. + +This feature helps reduce the size of deployments drastically. Previously, when deploying with Docker you would need to have all files from your package's `dependencies` installed to run `next start`. Starting with Next.js 12, you can leverage Output File Tracing in the `.next/` directory to only include the necessary files. + +Furthermore, this removes the need for the deprecated `serverless` target which can cause various issues and also creates unnecessary duplication. + +## How It Works + +During `next build`, Next.js will use [`@vercel/nft`](https://github.com/vercel/nft) to statically analyze `import`, `require`, and `fs` usage to determine all files that a page might load. + +Next.js' production server is also traced for its needed files and output at `.next/next-server.js.nft.json` which can be leveraged in production. + +To leverage the `.nft.json` files emitted to the `.next` output directory, you can read the list of files in each trace which are relative to the `.nft.json` file and then copy them to your deployment location. + +## Automatically Copying Traced Files (experimental) + +Next.js can automatically create a `standalone` folder which copies only the necessary files for a production deployment including select files in `node_modules`. + +To leverage this automatic copying you can enable it in your `next.config.js`: + +```js +module.exports = { + experimental: { + outputStandalone: true, + }, +} +``` + +This will create a folder at `.next/standalone` which can then be deployed on it's own without installing `node_modules`. + +Additionally, a minimal `server.js` file is also output which can be used instead of `next start`. This minimal server does not copy the `.next/static` directory by default as this should ideally be handled by a CDN instead, although it can be copied to the `standalone` folder manually and the `server.js` file will serve it automatically. + +## Caveats + +- While tracing in monorepo setups, the project directory is used for tracing by default. For `next build packages/web-app`, `packages/web-app` would be the tracing root and any files outside of that folder will not be included. To include files outside of this folder you can set `experimental.outputFileTracingRoot` in your `next.config.js`. + +```js +// packages/web-app/next.config.js +module.exports = { + experimental: { + // this includes files from the monorepo base two directories up + outputFileTracingRoot: path.join(__dirname, '../../'), + }, +} +``` + +- There are some cases that Next.js might fail to include required files, or might incorrectly include unused files. In those cases, you can export page configs props `unstable_includeFiles` and `unstable_excludeFiles` respectively. Each prop accepts an array of [globs](<https://en.wikipedia.org/wiki/Glob_(programming)>) relative to the project's root to either include or exclude in the trace. +- Currently, Next.js does not do anything with the emitted `.nft.json` files. The files must be read by your deployment platform, for example [Vercel](https://vercel.com), to create a minimal deployment. In a future release, a new command is planned to utilize these `.nft.json` files. diff --git a/docs/advanced-features/preview-mode.md b/docs/advanced-features/preview-mode.md index 98d1ae6698cb..8ef9c7a9fe1d 100644 --- a/docs/advanced-features/preview-mode.md +++ b/docs/advanced-features/preview-mode.md @@ -23,10 +23,11 @@ description: Next.js has the preview mode for statically generated pages. You ca <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app//">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-umbraco-heartcore">Umbraco Heartcore Example</a> (<a href="https://next-blog-umbraco-heartcore.vercel.app/">Demo</a>)</li> </ul> </details> -In the [Pages documentation](/docs/basic-features/pages.md) and the [Data Fetching documentation](/docs/basic-features/data-fetching.md), we talked about how to pre-render a page at build time (**Static Generation**) using `getStaticProps` and `getStaticPaths`. +In the [Pages documentation](/docs/basic-features/pages.md) and the [Data Fetching documentation](/docs/basic-features/data-fetching/overview.md), we talked about how to pre-render a page at build time (**Static Generation**) using `getStaticProps` and `getStaticPaths`. Static Generation is useful when your pages fetch data from a headless CMS. However, it’s not ideal when you’re writing a draft on your headless CMS and want to **preview** the draft immediately on your page. You’d want Next.js to render these pages at **request time** instead of build time and fetch the draft content instead of the published content. You’d want Next.js to bypass Static Generation only for this specific case. @@ -185,6 +186,8 @@ export default function handler(req, res) { } ``` +> **Note:** If calling this route using `Link` component, you must pass in `prefetch={false}` to prevent calling `clearPreviewData` during prefetch. + ### Specify the preview mode duration `setPreviewData` takes an optional second parameter which should be an options object. It accepts the following keys: @@ -229,7 +232,7 @@ This ensures that the bypass cookie can’t be guessed. The following pages might also be useful. <div class="card"> - <a href="/docs/basic-features/data-fetching.md"> + <a href="/docs/basic-features/data-fetching/overview.md"> <b>Data Fetching:</b> <small>Learn more about data fetching in Next.js.</small> </a> diff --git a/docs/advanced-features/react-18.md b/docs/advanced-features/react-18.md new file mode 100644 index 000000000000..1d82c82b71b4 --- /dev/null +++ b/docs/advanced-features/react-18.md @@ -0,0 +1,154 @@ +# React 18 + +[React 18](https://reactjs.org/blog/2021/06/08/the-plan-for-react-18.html) adds new features including, Suspense, automatic batching of updates, APIs like `startTransition`, and a new streaming API for server rendering with support for `React.lazy`. + +React 18 is in RC now. Read more about React 18's [release plan](https://github.com/reactwg/react-18/discussions) and discussions from the [working group](https://github.com/reactwg/react-18/discussions). + +### React 18 Usage in Next.js + +Ensure you have the `rc` npm tag of React installed: + +```jsx +npm install next@latest react@rc react-dom@rc +``` + +That's all! You can now start using React 18's new APIs like `startTransition` and `Suspense` in Next.js. + +### Enable SSR Streaming (Alpha) + +Concurrent features in React 18 include built-in support for server-side Suspense and SSR streaming support, allowing you to server-render pages using HTTP streaming. + +This is an experimental feature in Next.js 12, but once enabled, SSR will use the same [Edge Runtime](/docs/api-reference/edge-runtime.md) as [Middleware](/docs/middleware.md). + +To enable, use the experimental flag `concurrentFeatures: true`: + +```jsx +// next.config.js +module.exports = { + experimental: { + concurrentFeatures: true, + }, +} +``` + +Once enabled, you can use Suspense and SSR streaming for all pages. This also means that you can use Suspense-based data-fetching, `next/dynamic`, and React's built-in `React.lazy` with Suspense boundaries. + +```jsx +import dynamic from 'next/dynamic' +import { lazy, Suspense } from 'react' + +import Content from '../components/content' + +// These two ways are identical: +const Profile = dynamic(() => import('./profile'), { suspense: true }) +const Footer = lazy(() => import('./footer')) + +export default function Home() { + return ( + <div> + <Suspense fallback={<Spinner />}> + {/* A component that uses Suspense-based */} + <Content /> + </Suspense> + <Suspense fallback={<Spinner />}> + <Profile /> + </Suspense> + <Suspense fallback={<Spinner />}> + <Footer /> + </Suspense> + </div> + ) +} +``` + +## React Server Components + +React Server Components allow us to render everything, including the components themselves, on the server. This is fundamentally different from server-side rendering where you're pre-generating HTML on the server. With Server Components, there's **zero client-side JavaScript needed,** making page rendering faster. This improves the user experience of your application, pairing the best parts of server-rendering with client-side interactivity. + +### Enable React Server Components (Alpha) + +To use React Server Components, ensure you have React 18 installed. Then, turn on the `concurrentFeatures` and `serverComponents` options in `next.config.js`: + +```jsx +// next.config.js +module.exports = { + experimental: { + concurrentFeatures: true, + serverComponents: true, + }, +} +``` + +Next, if you already have customized `pages/_document` component, you need to remove the `getInitialProps` static method and the `getServerSideProps` export if there’s any, otherwise it won't work with server components. If no custom Document component is provided, Next.js will fallback to a default one like below. + +```jsx +// pages/_document.js +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + <Html> + <Head /> + <body> + <Main /> + <NextScript /> + </body> + </Html> + ) +} +``` + +Then, you can start using React Server Components. [See our example](https://github.com/vercel/next-rsc-demo) for more information. + +### Server Components APIs (Alpha) + +To run a component on the server, append `.server.js` to the end of the filename. For example `./pages/home.server.js` is a Server Component. + +For client components, add `.client.js`. For example, `./components/avatar.client.js`. + +You can then import other server or client components from any server component. Note: a server component **can not** be imported by a client component. Components without "server/client" extensions will be treated as "universal component" and can be used and rendered by both sides, depending on where it is imported. For example: + +```jsx +// pages/home.server.js + +import { Suspense } from 'react' + +import Profile from '../components/profile.server' +import Content from '../components/content.client' + +export default function Home() { + return ( + <div> + <h1>Welcome to React Server Components</h1> + <Suspense fallback={'Loading...'}> + <Profile /> + </Suspense> + <Content /> + </div> + ) +} +``` + +The `<Home>` and `<Profile>` components will always be server-side rendered and streamed to the client, and will not be included by the client runtime. However `<Content>` will still be hydrated on the client-side, like normal React components. + +To see a full example, check out [link to the demo and repository](https://github.com/vercel/next-rsc-demo). + +## **Supported Next.js APIs** + +- `next/link` / `next/image` +- `next/document` / `next/app` +- Dynamic routing + +## **Unsupported Next.js APIs** + +While RSC and SSR streaming is still in the alpha stage, not all Next.js APIs are supported. The following Next.js APIs have limited functionality inside Server Components: + +- React internals: Most of React hooks such as `useContext`, `useState`, `useReducer`, `useEffect` and `useLayoutEffect` are not supported as of today since Server Components are executed per requests and aren't stateful. +- `next/head` +- Partial: Note that Inside `.client.js` components `useRouter` is supported +- Styled JSX +- CSS Modules +- Next.js I18n +- `getInitialProps`, `getStaticProps` and `getStaticPaths` + +React 18 without SSR streaming isn't affected. diff --git a/docs/advanced-features/security-headers.md b/docs/advanced-features/security-headers.md index c51718bdcb08..521ab7be2869 100644 --- a/docs/advanced-features/security-headers.md +++ b/docs/advanced-features/security-headers.md @@ -18,7 +18,7 @@ module.exports = { return [ { // Apply these headers to all routes in your application. - source: '/(.*)', + source: '/:path*', headers: securityHeaders, }, ] @@ -43,7 +43,7 @@ This header controls DNS prefetching, allowing browsers to proactively perform d This header informs browsers it should only be accessed using HTTPS, instead of using HTTP. Using the configuration below, all present and future subdomains will use HTTPS for a `max-age` of 2 years. This blocks access to pages or subdomains that can only be served over HTTP. -If you're deploying to [Vercel](https://vercel.com/docs/edge-network/headers#strict-transport-security), this header is not necessary as it's automatically added to all deployments. +If you're deploying to [Vercel](https://vercel.com/docs/edge-network/headers#strict-transport-security), this header is not necessary as it's automatically added to all deployments unless you declare [`headers`](/docs/api-reference/next.config.js/headers.md) in your `next.config.js`. ```jsx { @@ -113,10 +113,29 @@ This header helps prevent cross-site scripting (XSS), clickjacking and other cod You can read about the many different CSP options [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). +You can add Content Security Policy directives using a template string. + +```jsx +// Before defining your Security Headers +// add Content Security Policy directives using a template string. + +const ContentSecurityPolicy = ` + default-src 'self'; + script-src 'self'; + child-src example.com; + style-src 'self' example.com; + font-src 'self'; +` +``` + +When a directive uses a keyword such as `self`, wrap it in single quotes `''`. + +In the header's value, replace the new line with an empty string. + ```jsx { key: 'Content-Security-Policy', - value: // Your CSP Policy + value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim() } ``` diff --git a/docs/advanced-features/src-directory.md b/docs/advanced-features/src-directory.md index 00a162832382..6bb496833fd5 100644 --- a/docs/advanced-features/src-directory.md +++ b/docs/advanced-features/src-directory.md @@ -11,7 +11,7 @@ The `src` directory is very common in many apps and Next.js supports it by defau ## Caveats - `src/pages` will be ignored if `pages` is present in the root directory -- Config files like `next.config.js` and `tsconfig.json` should be inside the root directory, moving them to `src` won't work. Same goes for the [`public` directory](/docs/basic-features/static-file-serving.md) +- Config files like `next.config.js` and `tsconfig.json`, as well as environment variables, should be inside the root directory, moving them to `src` won't work. Same goes for the [`public` directory](/docs/basic-features/static-file-serving.md) ## Related diff --git a/docs/advanced-features/static-html-export.md b/docs/advanced-features/static-html-export.md index c217b55d952a..6408adf0b7c6 100644 --- a/docs/advanced-features/static-html-export.md +++ b/docs/advanced-features/static-html-export.md @@ -11,27 +11,13 @@ description: Export your Next.js app to static HTML, and run it standalone witho </ul> </details> -`next export` allows you to export your app to static HTML, which can be run standalone without the need of a Node.js server. +`next export` allows you to export your Next.js application to static HTML, which can be run standalone without the need of a Node.js server. It is recommended to only use `next export` if you don't need any of the [unsupported features](#unsupported-features) requiring a server. -The exported app supports almost every feature of Next.js, including dynamic routes, prefetching, preloading and dynamic imports. +If you're looking to build a hybrid site where only _some_ pages are prerendered to static HTML, Next.js already does that automatically. Learn more about [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) and [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md). -`next export` works by prerendering all pages to HTML. For [dynamic routes](/docs/routing/dynamic-routes.md), your page can export a [`getStaticPaths`](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation) function to let the exporter know which HTML pages to generate for that route. +## `next export` -> `next export` is intended for scenarios where **none** of your pages have server-side or incremental data requirements (though statically-rendered pages can still [fetch data on the client side](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side)). -> -> If you're looking to make a hybrid site where only _some_ pages are prerendered to static HTML, Next.js already does that automatically for you! Read up on [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) for details. -> -> `next export` also causes features like [Incremental Static Generation](/docs/basic-features/data-fetching.md#fallback-true) and [Regeneration](/docs/basic-features/data-fetching.md#incremental-static-regeneration) to be disabled, as they require [`next start`](/docs/api-reference/cli.md#production) or a serverless deployment to function. - -## How to use it - -Develop your app as you normally do with Next.js. Then run: - -```bash -next build && next export -``` - -For that you may want to update the scripts in your `package.json` like this: +Update your build script in `package.json` to use `next export`: ```json "scripts": { @@ -39,37 +25,48 @@ For that you may want to update the scripts in your `package.json` like this: } ``` -And run it with: +Running `npm run build` will generate an `out` directory. -```bash -npm run build -``` - -Then you'll have a static version of your app in the `out` directory. +`next export` builds an HTML version of your app. During `next build`, [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) and [`getStaticPaths`](/docs/basic-features/data-fetching/get-static-paths.md) will generate an HTML file for each page in your `pages` directory (or more for [dynamic routes](/docs/routing/dynamic-routes.md). Then, `next export` will copy the already exported files into the correct directory. `getInitialProps` will generate the HTML files during `next export` instead of `next build`. -By default `next export` doesn't require any configuration. -It will output a static HTML file for each page in your `pages` directory (or more for [dynamic routes](/docs/routing/dynamic-routes.md), where it will call [`getStaticPaths`](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation) and generate pages based on the result). For more advanced scenarios, you can define a parameter called [`exportPathMap`](/docs/api-reference/next.config.js/exportPathMap.md) in your [`next.config.js`](/docs/api-reference/next.config.js/introduction.md) file to configure exactly which pages will be generated. -## Deployment +## Supported Features + +The majority of core Next.js features needed to build a static site are supported, including: + +- [Dynamic Routes when using `getStaticPaths`](/docs/routing/dynamic-routes.md) +- Prefetching with `next/link` +- Preloading JavaScript +- [Dynamic Imports](/docs/advanced-features/dynamic-import.md) +- Any styling options (e.g. CSS Modules, styled-jsx) +- [Client-side data fetching](/docs/basic-features/data-fetching/client-side.md) +- [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) +- [`getStaticPaths`](/docs/basic-features/data-fetching/get-static-paths.md) +- [Image Optimization](/docs/basic-features/image-optimization.md) using a [custom loader](/docs/basic-features/image-optimization.md#loader) -By default, `next export` will generate an `out` directory, which can be served by any static hosting service or CDN. +## Unsupported Features -> We strongly recommend using [Vercel](https://vercel.com/) even if your Next.js app is fully static. [Vercel](https://vercel.com/) is optimized to make static Next.js apps blazingly fast. `next export` works with Zero Config deployments on Vercel. +Features that require a Node.js server, or dynamic logic that cannot be computed during the build process, are not supported: -## Caveats +- [Image Optimization](/docs/basic-features/image-optimization.md) (default loader) +- [Internationalized Routing](/docs/advanced-features/i18n-routing.md) +- [API Routes](/docs/api-routes/introduction.md) +- [Rewrites](/docs/api-reference/next.config.js/rewrites.md) +- [Redirects](/docs/api-reference/next.config.js/redirects.md) +- [Headers](/docs/api-reference/next.config.js/headers.md) +- [Middleware](/docs/middleware.md) +- [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) +- [`fallback: true`](/docs/api-reference/data-fetching/get-static-paths.md#fallback-true) +- [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md) -- With `next export`, we build an HTML version of your app. At export time, we call [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) for each page that exports it, and pass the result to the page's component. It's also possible to use the older [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md) API instead of `getStaticProps`, but it comes with a few caveats: +### `getInitialProps` - - `getInitialProps` cannot be used alongside `getStaticProps` or `getStaticPaths` on any given page. If you have dynamic routes, instead of using `getStaticPaths` you'll need to configure the [`exportPathMap`](/docs/api-reference/next.config.js/exportPathMap.md) parameter in your [`next.config.js`](/docs/api-reference/next.config.js/introduction.md) file to let the exporter know which HTML files it should output. - - When `getInitialProps` is called during export, the `req` and `res` fields of its [`context`](/docs/api-reference/data-fetching/getInitialProps.md#context-object) parameter will be empty objects, since during export there is no server running. - - `getInitialProps` **will be called on every client-side navigation**, if you'd like to only fetch data at build-time, switch to `getStaticProps`. - - `getInitialProps` should fetch from an API and cannot use Node.js-specific libraries or the file system like `getStaticProps` can. +It's possible to use the [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md) API instead of `getStaticProps`, but it comes with a few caveats: - It's recommended to use and migrate towards `getStaticProps` over `getInitialProps` whenever possible. +- `getInitialProps` cannot be used alongside `getStaticProps` or `getStaticPaths` on any given page. If you have dynamic routes, instead of using `getStaticPaths` you'll need to configure the [`exportPathMap`](/docs/api-reference/next.config.js/exportPathMap.md) parameter in your [`next.config.js`](/docs/api-reference/next.config.js/introduction.md) file to let the exporter know which HTML files it should output. +- When `getInitialProps` is called during export, the `req` and `res` fields of its [`context`](/docs/api-reference/data-fetching/get-initial-props.md#context-object) parameter will be empty objects, since during export there is no server running. +- `getInitialProps` **will be called on every client-side navigation**, if you'd like to only fetch data at build-time, switch to `getStaticProps`. +- `getInitialProps` should fetch from an API and cannot use Node.js-specific libraries or the file system like `getStaticProps` can. -- The [`fallback: true`](/docs/basic-features/data-fetching.md#fallback-true) mode of `getStaticPaths` is not supported when using `next export`. -- [API Routes](/docs/api-routes/introduction.md) are not supported by this method because they can't be prerendered to HTML. -- [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) cannot be used within pages because the method requires a server. Consider using [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) instead. -- [Internationalized Routing](/docs/advanced-features/i18n-routing.md) is not supported as it requires Next.js' server-side routing. -- The [`next/image`](/docs/api-reference/next/image.md) component's default loader is not supported when using `next export`. However, other [loader](/docs/basic-features/image-optimization.md#loader) options will work. +We recommend migrating towards `getStaticProps` over `getInitialProps` whenever possible. diff --git a/docs/advanced-features/using-mdx.md b/docs/advanced-features/using-mdx.md new file mode 100644 index 000000000000..f8e932266e8f --- /dev/null +++ b/docs/advanced-features/using-mdx.md @@ -0,0 +1,194 @@ +--- +description: Learn how to use @next/mdx in your Next.js project. +--- + +# Using MDX with Next.js + +MDX is a superset of markdown that lets you write JSX directly in your markdown files. It is a powerful way to add dynamic interactivity, and embed components within your content, helping you to bring your pages to life. + +Next.js supports MDX through a number of different means, this page will outline some of the ways you can begin integrating MDX into your Next.js project. + +## Why use MDX? + +Authoring in markdown is an intuitive way to write content, its terse syntax, once adopted, can enable you to write content that is both readable and maintainable. Because you can use `HTML` elements in your markdown, you can also get creative when styling your markdown pages. + +However, because markdown is essentially static content, you can't create _dynamic_ content based on user interactivity. Where MDX shines is in its ability to let you create and use your React components directly in the markup. This opens up a wide range of possibilities when composing your sites pages with interactivity in mind. + +## MDX Plugins + +Internally MDX uses remark and rehype. Remark is a markdown processor powered by a plugins ecosystem. This plugin ecosystem lets you parse code, transform `HTML` elements, change syntax, extract frontmatter, and more. + +Rehype is an `HTML` processor, also powered by a plugin ecosystem. Similar to remark, these plugins let you manipulate, sanitize, compile and configure all types of data, elements and content. + +To use a plugin from either remark or rehype, you will need to add it to the MDX packages config. + +## `@next/mdx` + +The `@next/mdx` package is configured in the `next.config.js` file at your projects root. **It sources data from local files**, allowing you to create pages with a `.mdx` extension, directly in your `/pages` directory. + +### Setup `@next/mdx` in Next.js + +The following steps outline how to setup `@next/mdx` in your Next.js project: + +1. Install the required packages: + + ```bash + npm install @next/mdx @mdx-js/loader + ``` + +2. Require the package and configure to support top level `.mdx` pages. The following adds the `options` object key allowing you to pass in any plugins: + + ```js + // next.config.js + + const withMDX = require('@next/mdx')({ + extension: /\.mdx?$/, + options: { + remarkPlugins: [], + rehypePlugins: [], + // If you use `MDXProvider`, uncomment the following line. + // providerImportSource: "@mdx-js/react", + }, + }) + module.exports = withMDX({ + pageExtensions: ['js', 'jsx', 'md', 'mdx'], + }) + ``` + +3. Create a new MDX page within the `/pages` directory: + + ```bash + - /pages + - my-mdx-page.mdx + - package.json + ``` + +## Using Components, Layouts and Custom Elements + +You can now import a React component directly inside your MDX page: + +```md +import { MyComponent } from 'my-components' + +# My MDX page + +This is a list in markdown: + +- One +- Two +- Three + +Checkout my React component: + +<MyComponent/> +``` + +### Frontmatter + +Frontmatter is a YAML like key/value pairing that can be used to store data about a page. `@next/mdx` does **not** support frontmatter by default, though there are many solutions for adding frontmatter to your MDX content, such as [gray-matter](https://github.com/jonschlinkert/gray-matter). + +To access page metadata with `@next/mdx`, you can export a meta object from within the `.mdx` file: + +```md +export const meta = { +author: 'Rich Haines' +} + +# My MDX page +``` + +### Layouts + +To add a layout to your MDX page, create a new component and import it into the MDX page. Then you can wrap the MDX page with your layout component: + +```md +import { MyComponent, MyLayoutComponent } from 'my-components' + +export const meta = { +author: 'Rich Haines' +} + +# My MDX Page with a Layout + +This is a list in markdown: + +- One +- Two +- Three + +Checkout my React component: + +<MyComponent/> + +export default ({ children }) => <MyLayoutComponent meta={meta}>{children}</MyLayoutComponent> +``` + +### Custom Elements + +One of the pleasant aspects of using markdown, is that it maps to native `HTML` elements, making writing fast, and intuitive: + +```md +# H1 heading + +## H2 heading + +This is a list in markdown: + +- One +- Two +- Three +``` + +The above generates the following `HTML`: + +```html +<h1>H1 heading</h1> + +<h2>H2 heading</h2> + +<p>This is a list in markdown:</p> + +<ul> + <li>One</li> + <li>Two</li> + <li>Three</li> +</ul> +``` + +When you want to style your own elements to give a custom feel to your website or application, you can pass in shortcodes. These are your own custom components that map to `HTML` elements. To do this you use the `MDXProvider` and pass a components object as a prop. Each object key in the components object maps to a `HTML` element name. You also need to specify `providerImportSource: "@mdx-js/react"` in `next.config.js`. + +```jsx +// pages/index.js + +import { MDXProvider } from '@mdx-js/react' +import Image from 'next/image' +import { Heading, Text, Pre, Code, Table } from 'my-components' + +const ResponsiveImage = (props) => ( + <Image alt={props.alt} layout="responsive" {...props} /> +) + +const components = { + img: ResponsiveImage, + h1: Heading.H1, + h2: Heading.H2, + p: Text, + code: Pre, + inlineCode: Code, +} + +export default function Post(props) { + return ( + <MDXProvider components={components}> + <main {...props} /> + </MDXProvider> + ) +} +``` + +## Helpful Links + +- [MDX](https://mdxjs.com) +- [`@next/mdx`](https://www.npmjs.com/package/@next/mdx) +- [remark](https://github.com/remarkjs/remark) +- [rehype](https://github.com/rehypejs/rehype) diff --git a/docs/api-reference/cli.md b/docs/api-reference/cli.md index b79edb5f0bbc..20b36920d7bf 100644 --- a/docs/api-reference/cli.md +++ b/docs/api-reference/cli.md @@ -21,7 +21,7 @@ Usage $ next <command> Available commands - build, start, export, dev, lint, telemetry + build, start, export, dev, lint, telemetry, info Options --version, -v Version number @@ -125,3 +125,36 @@ Next.js collects **completely anonymous** telemetry data about general usage. Participation in this anonymous program is optional, and you may opt-out if you'd not like to share any information. To learn more about Telemetry, [please read this document](https://nextjs.org/telemetry/). + +## Info + +`next info` prints relevant details about the current system which can be used to report Next.js bugs. +This information includes Operating System platform/arch/version, Binaries (Node.js, npm, Yarn, pnpm) and npm package versions (`next`, `react`, `react-dom`). + +Running the following in your project's root directory: + +```bash +next info +``` + +will give you information like this example: + +```bash + + Operating System: + Platform: linux + Arch: x64 + Version: #22-Ubuntu SMP Fri Nov 5 13:21:36 UTC 2021 + Binaries: + Node: 16.13.0 + npm: 8.1.0 + Yarn: 1.22.17 + pnpm: 6.24.2 + Relevant packages: + next: 12.0.8 + react: 17.0.2 + react-dom: 17.0.2 + +``` + +This information should then be pasted into GitHub Issues. diff --git a/docs/api-reference/create-next-app.md b/docs/api-reference/create-next-app.md index 1192865d09c0..f463840b88b9 100644 --- a/docs/api-reference/create-next-app.md +++ b/docs/api-reference/create-next-app.md @@ -7,7 +7,7 @@ description: Create Next.js apps in one command with create-next-app. The easiest way to get started with Next.js is by using `create-next-app`. This CLI tool enables you to quickly start building a new Next.js application, with everything set up for you. You can create a new app using the default Next.js template, or by using one of the [official Next.js examples](https://github.com/vercel/next.js/tree/canary/examples). To get started, use the following command: ```bash -npx create-next-app +npx create-next-app@latest # or yarn create next-app ``` @@ -15,7 +15,7 @@ yarn create next-app You can create a [TypeScript project](https://github.com/vercel/next.js/blob/canary/docs/basic-features/typescript.md) with the `--ts, --typescript` flag: ```bash -npx create-next-app --ts +npx create-next-app@latest --ts # or yarn create next-app --typescript ``` @@ -25,7 +25,7 @@ yarn create next-app --typescript `create-next-app` comes with the following options: - **--ts, --typescript** - Initialize as a TypeScript project. -- **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/master/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. +- **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/canary/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. - **--example-path [path-to-example]** - In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: `--example-path foo/bar` - **--use-npm** - Explicitly tell the CLI to bootstrap the app using npm. To bootstrap using yarn we recommend running `yarn create next-app` @@ -33,7 +33,7 @@ yarn create next-app --typescript `create-next-app` allows you to create a new Next.js app within seconds. It is officially maintained by the creators of Next.js, and includes a number of benefits: -- **Interactive Experience**: Running `npx create-next-app` (with no arguments) launches an interactive experience that guides you through setting up a project. +- **Interactive Experience**: Running `npx create-next-app@latest` (with no arguments) launches an interactive experience that guides you through setting up a project. - **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. - **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. - **Support for Examples**: Create Next App can bootstrap your application using an example from the Next.js examples collection (e.g. `npx create-next-app --example api-routes`). diff --git a/docs/api-reference/data-fetching/get-initial-props.md b/docs/api-reference/data-fetching/get-initial-props.md new file mode 100644 index 000000000000..004ba283a4ff --- /dev/null +++ b/docs/api-reference/data-fetching/get-initial-props.md @@ -0,0 +1,126 @@ +--- +description: Enable Server-Side Rendering in a page and do initial data population with `getInitialProps`. +--- + +# getInitialProps + +**Recommended: [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) or [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md)** instead of `getInitialProps`. These data fetching methods allow you to have a granular choice between static generation and server-side rendering. + +`getInitialProps` enables [server-side rendering](/docs/basic-features/pages.md#server-side-rendering) in a page and allows you to do **initial data population**, it means sending the [page](/docs/basic-features/pages.md) with the data already populated from the server. This is especially useful for [SEO](https://en.wikipedia.org/wiki/Search_engine_optimization). + +`getInitialProps` will disable [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md). + +`getInitialProps` is an [`async`](https://vercel.com/blog/async-and-await) function that can be added to any page as a [`static method`](https://javascript.info/static-properties-methods). Take a look at the following example: + +```jsx +function Page({ stars }) { + return <div>Next stars: {stars}</div> +} + +Page.getInitialProps = async (ctx) => { + const res = await fetch('https://api.github.com/repos/vercel/next.js') + const json = await res.json() + return { stars: json.stargazers_count } +} + +export default Page +``` + +Or using a class component: + +```jsx +import React from 'react' + +class Page extends React.Component { + static async getInitialProps(ctx) { + const res = await fetch('https://api.github.com/repos/vercel/next.js') + const json = await res.json() + return { stars: json.stargazers_count } + } + + render() { + return <div>Next stars: {this.props.stars}</div> + } +} + +export default Page +``` + +`getInitialProps` is used to asynchronously fetch some data, which then populates `props`. + +Data returned from `getInitialProps` is serialized when server rendering, similar to what [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) does. Make sure the returned object from `getInitialProps` is a plain `Object` and not using `Date`, `Map` or `Set`. + +For the initial page load, `getInitialProps` will run on the server only. `getInitialProps` will then run on the client when navigating to a different route via the [`next/link`](/docs/api-reference/next/link.md) component or by using [`next/router`](/docs/api-reference/next/router.md). However, if `getInitialProps` is used in a custom `_app.js`, and the page being navigated to implements `getServerSideProps`, then `getInitialProps` will run on the server. + +## Context Object + +`getInitialProps` receives a single argument called `context`, it's an object with the following properties: + +- `pathname` - Current route. That is the path of the page in `/pages` +- `query` - Query string section of URL parsed as an object +- `asPath` - `String` of the actual path (including the query) shown in the browser +- `req` - [HTTP request object](https://nodejs.org/api/http.html#http_class_http_incomingmessage 'Class: http.IncomingMessage HTTP | Node.js v14.8.0 Documentation') (server only) +- `res` - [HTTP response object](https://nodejs.org/api/http.html#http_class_http_serverresponse 'Class: http.ServerResponse HTTP | Node.js v14.8.0 Documentation') (server only) +- `err` - Error object if any error is encountered during the rendering + +## Caveats + +- `getInitialProps` can **not** be used in children components, only in the default export of every page +- If you are using server-side only modules inside `getInitialProps`, make sure to [import them properly](https://arunoda.me/blog/ssr-and-server-only-modules), otherwise it'll slow down your app + +## TypeScript + +If you're using TypeScript, you can use the `NextPage` type for function components: + +```jsx +import { NextPage } from 'next' + +interface Props { + userAgent?: string; +} + +const Page: NextPage<Props> = ({ userAgent }) => ( + <main>Your user agent: {userAgent}</main> +) + +Page.getInitialProps = async ({ req }) => { + const userAgent = req ? req.headers['user-agent'] : navigator.userAgent + return { userAgent } +} + +export default Page +``` + +And for `React.Component`, you can use `NextPageContext`: + +```jsx +import React from 'react' +import { NextPageContext } from 'next' + +interface Props { + userAgent?: string; +} + +export default class Page extends React.Component<Props> { + static async getInitialProps({ req }: NextPageContext) { + const userAgent = req ? req.headers['user-agent'] : navigator.userAgent + return { userAgent } + } + + render() { + const { userAgent } = this.props + return <main>Your user agent: {userAgent}</main> + } +} +``` + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/basic-features/data-fetching/overview.md"> + <b>Data Fetching:</b> + <small>Learn more about data fetching in Next.js.</small> + </a> +</div> diff --git a/docs/api-reference/data-fetching/get-server-side-props.md b/docs/api-reference/data-fetching/get-server-side-props.md new file mode 100644 index 000000000000..d7e7c1ee6ef5 --- /dev/null +++ b/docs/api-reference/data-fetching/get-server-side-props.md @@ -0,0 +1,151 @@ +--- +description: API reference for `getServerSideProps`. Learn how to fetch data on each request with Next.js. +--- + +# getServerSideProps + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ------------------------------------------------------------------- | +| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | +| `v9.3.0` | `getServerSideProps` introduced. | + +</details> + +When exporting a function called `getServerSideProps` (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by `getServerSideProps`. This is useful if you want to fetch data that changes often, and have the page update to show the most current data. + +```js +export async function getServerSideProps(context) { + return { + props: {}, // will be passed to the page component as props + } +} +``` + +You can import modules in top-level scope for use in `getServerSideProps`. Imports used will **not be bundled for the client-side**. This means you can write **server-side code directly in `getServerSideProps`**, including fetching data from your database. + +## Context parameter + +The `context` parameter is an object containing the following keys: + +- `params`: If this page uses a [dynamic route](/docs/routing/dynamic-routes.md), `params` contains the route parameters. If the page name is `[id].js` , then `params` will look like `{ id: ... }`. +- `req`: [The `HTTP` IncomingMessage object](https://nodejs.org/api/http.html#http_class_http_incomingmessage). +- `res`: [The `HTTP` response object](https://nodejs.org/api/http.html#http_class_http_serverresponse). +- `query`: An object representing the query string. +- `preview`: `preview` is `true` if the page is in the [Preview Mode](/docs/advanced-features/preview-mode.md) and `false` otherwise. +- `previewData`: The [preview](/docs/advanced-features/preview-mode.md) data set by `setPreviewData`. +- `resolvedUrl`: A normalized version of the request `URL` that strips the `_next/data` prefix for client transitions and includes original query values. +- `locale` contains the active locale (if enabled). +- `locales` contains all supported locales (if enabled). +- `defaultLocale` contains the configured default locale (if enabled). + +## getServerSideProps return values + +The `getServerSideProps` function should return an object with **any one of the following** properties: + +### `props` + +The `props` object is a key-value pair, where each value is received by the page component. It should be a [serializable object](https://developer.mozilla.org/en-US/docs/Glossary/Serialization) so that any props passed, could be serialized with [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). + +```jsx +export async function getServerSideProps(context) { + return { + props: { message: `Next.js is awesome` }, // will be passed to the page component as props + } +} +``` + +### `notFound` + +The `notFound` boolean allows the page to return a `404` status and [404 Page](/docs/advanced-features/custom-error-page.md#404-page). With `notFound: true`, the page will return a `404` even if there was a successfully generated page before. This is meant to support use cases like user-generated content getting removed by its author. + +```js +export async function getServerSideProps(context) { + const res = await fetch(`https://.../data`) + const data = await res.json() + + if (!data) { + return { + notFound: true, + } + } + + return { + props: { data }, // will be passed to the page component as props + } +} +``` + +### `redirect` + +The `redirect` object allows redirecting to internal and external resources. It should match the shape of `{ destination: string, permanent: boolean }`. In some rare cases, you might need to assign a custom status code for older `HTTP` clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both. + +```js +export async function getServerSideProps(context) { + const res = await fetch(`https://.../data`) + const data = await res.json() + + if (!data) { + return { + redirect: { + destination: '/', + permanent: false, + }, + } + } + + return { + props: {}, // will be passed to the page component as props + } +} +``` + +### getServerSideProps with TypeScript + +For TypeScript, you can use the `GetServerSideProps` type from `next`: + +```ts +import { GetServerSideProps } from 'next' + +export const getServerSideProps: GetServerSideProps = async (context) => { + // ... +} +``` + +If you want to get inferred typings for your props, you can use `InferGetServerSidePropsType<typeof getServerSideProps>`: + +```tsx +import { InferGetServerSidePropsType } from 'next' + +type Data = { ... } + +export const getServerSideProps = async () => { + const res = await fetch('https://.../data') + const data: Data = await res.json() + + return { + props: { + data, + }, + } +} + +function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) { + // will resolve posts to type Data +} + +export default Page +``` + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/basic-features/data-fetching/overview.md"> + <b>Data Fetching:</b> + <small>Learn more about data fetching in Next.js.</small> + </a> +</div> diff --git a/docs/api-reference/data-fetching/get-static-paths.md b/docs/api-reference/data-fetching/get-static-paths.md new file mode 100644 index 000000000000..9175bed28a72 --- /dev/null +++ b/docs/api-reference/data-fetching/get-static-paths.md @@ -0,0 +1,212 @@ +--- +description: API reference for `getStaticPaths`. Learn how to fetch data and generate static pages with `getStaticPaths`. +--- + +# getStaticPaths + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| -------- | --------------------------------------------------------------------------------------------------------------- | +| `v9.5.0` | Stable [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) | +| `v9.3.0` | `getStaticPaths` introduced. | + +</details> + +When exporting a function called `getStaticPaths` from a page that uses [Dynamic Routes](/docs/routing/dynamic-routes.md), Next.js will statically pre-render all the paths specified by `getStaticPaths`. + +```jsx +export async function getStaticPaths() { + return { + paths: [ + { params: { ... } } // See the "paths" section below + ], + fallback: true, false or "blocking" // See the "fallback" section below + }; +} +``` + +## getStaticPaths return values + +The `getStaticPaths` function should return an object with the following **required** properties: + +### `paths` + +The `paths` key determines which paths will be pre-rendered. For example, suppose that you have a page that uses [Dynamic Routes](/docs/routing/dynamic-routes.md) named `pages/posts/[id].js`. If you export `getStaticPaths` from this page and return the following for `paths`: + +```js +return { + paths: [ + { params: { id: '1' } }, + { params: { id: '2' } } + ], + fallback: ... +} +``` + +Then, Next.js will statically generate `/posts/1` and `/posts/2` during `next build` using the page component in `pages/posts/[id].js`. + +The value for each `params` object must match the parameters used in the page name: + +- If the page name is `pages/posts/[postId]/[commentId]`, then `params` should contain `postId` and `commentId`. +- If the page name uses [catch-all routes](/docs/routing/dynamic-routes.md#catch-all-routes) like `pages/[...slug]`, then `params` should contain `slug` (which is an array). If this array is `['hello', 'world']`, then Next.js will statically generate the page at `/hello/world`. +- If the page uses an [optional catch-all route](/docs/routing/dynamic-routes.md#optional-catch-all-routes), use `null`, `[]`, `undefined` or `false` to render the root-most route. For example, if you supply `slug: false` for `pages/[[...slug]]`, Next.js will statically generate the page `/`. + +### `fallback: false` + +If `fallback` is `false`, then any paths not returned by `getStaticPaths` will result in a **404 page**. + +When `next build` is run, Next.js will check if `getStaticPaths` returned `fallback: false`, it will then build **only** the paths returned by `getStaticPaths`. This option is useful if you have a small number of paths to create, or new page data is not added often. If you find that you need to add more paths, and you have `fallback: false`, you will need to run `next build` again so that the new paths can be generated. + +The following example pre-renders one blog post per page called `pages/posts/[id].js`. The list of blog posts will be fetched from a CMS and returned by `getStaticPaths`. Then, for each page, it fetches the post data from a CMS using [`getStaticProps`](/docs/api-reference/data-fetching/get-static-props.md). + +```jsx +// pages/posts/[id].js + +function Post({ post }) { + // Render post... +} + +// This function gets called at build time +export async function getStaticPaths() { + // Call an external API endpoint to get posts + const res = await fetch('https://.../posts') + const posts = await res.json() + + // Get the paths we want to pre-render based on posts + const paths = posts.map((post) => ({ + params: { id: post.id }, + })) + + // We'll pre-render only these paths at build time. + // { fallback: false } means other routes should 404. + return { paths, fallback: false } +} + +// This also gets called at build time +export async function getStaticProps({ params }) { + // params contains the post `id`. + // If the route is like /posts/1, then params.id is 1 + const res = await fetch(`https://.../posts/${params.id}`) + const post = await res.json() + + // Pass post data to the page via props + return { props: { post } } +} + +export default Post +``` + +### `fallback: true` + +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://static-tweet.vercel.app">Static generation of a large number of pages</a></li> + </ul> +</details> + +If `fallback` is `true`, then the behavior of `getStaticProps` changes in the following ways: + +- The paths returned from `getStaticPaths` will be rendered to `HTML` at build time by `getStaticProps`. +- The paths that have not been generated at build time will **not** result in a 404 page. Instead, Next.js will serve a [“fallback”](#fallback-pages) version of the page on the first request to such a path. Web crawlers, such as Google, won't be served a fallback and instead the path will behave as in [`fallback: 'blocking'`](#fallback-blocking). +- In the background, Next.js will statically generate the requested path `HTML` and `JSON`. This includes running `getStaticProps`. +- When complete, the browser receives the `JSON` for the generated path. This will be used to automatically render the page with the required props. From the user’s perspective, the page will be swapped from the fallback page to the full page. +- At the same time, Next.js adds this path to the list of pre-rendered pages. Subsequent requests to the same path will serve the generated page, like other pages pre-rendered at build time. + +> **Note:** `fallback: true` is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). + +#### When is `fallback: true` useful? + +`fallback: true` is useful if your app has a very large number of static pages that depend on data (such as a very large e-commerce site). If you want to pre-render all product pages, the builds would take a very long time. + +Instead, you may statically generate a small subset of pages and use `fallback: true` for the rest. When someone requests a page that is not generated yet, the user will see the page with a loading indicator or skeleton component. + +Shortly after, `getStaticProps` finishes and the page will be rendered with the requested data. From now on, everyone who requests the same page will get the statically pre-rendered page. + +This ensures that users always have a fast experience while preserving fast builds and the benefits of Static Generation. + +`fallback: true` will not _update_ generated pages, for that take a look at [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md). + +### `fallback: 'blocking'` + +If `fallback` is `'blocking'`, new paths not returned by `getStaticPaths` will wait for the `HTML` to be generated, identical to SSR (hence why _blocking_), and then be cached for future requests so it only happens once per path. + +`getStaticProps` will behave as follows: + +- The paths returned from `getStaticPaths` will be rendered to `HTML` at build time by `getStaticProps`. +- The paths that have not been generated at build time will **not** result in a 404 page. Instead, Next.js will SSR on the first request and return the generated `HTML`. +- When complete, the browser receives the `HTML` for the generated path. From the user’s perspective, it will transition from "the browser is requesting the page" to "the full page is loaded". There is no flash of loading/fallback state. +- At the same time, Next.js adds this path to the list of pre-rendered pages. Subsequent requests to the same path will serve the generated page, like other pages pre-rendered at build time. + +`fallback: 'blocking'` will not _update_ generated pages by default. To update generated pages, use [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) in conjunction with `fallback: 'blocking'`. + +> **Note:** `fallback: 'blocking'` is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). + +### Fallback pages + +In the “fallback” version of a page: + +- The page’s props will be empty. +- Using the [router](/docs/api-reference/next/router.md), you can detect if the fallback is being rendered, `router.isFallback` will be `true`. + +The following example showcases using `isFallback`: + +```jsx +// pages/posts/[id].js +import { useRouter } from 'next/router' + +function Post({ post }) { + const router = useRouter() + + // If the page is not yet generated, this will be displayed + // initially until getStaticProps() finishes running + if (router.isFallback) { + return <div>Loading...</div> + } + + // Render post... +} + +// This function gets called at build time +export async function getStaticPaths() { + return { + // Only `/posts/1` and `/posts/2` are generated at build time + paths: [{ params: { id: '1' } }, { params: { id: '2' } }], + // Enable statically generating additional pages + // For example: `/posts/3` + fallback: true, + } +} + +// This also gets called at build time +export async function getStaticProps({ params }) { + // params contains the post `id`. + // If the route is like /posts/1, then params.id is 1 + const res = await fetch(`https://.../posts/${params.id}`) + const post = await res.json() + + // Pass post data to the page via props + return { + props: { post }, + // Re-generate the post at most once per second + // if a request comes in + revalidate: 1, + } +} + +export default Post +``` + +## getStaticPaths with TypeScript + +For TypeScript, you can use the `GetStaticPaths` type from `next`: + +```ts +import { GetStaticPaths } from 'next' + +export const getStaticPaths: GetStaticPaths = async () => { + // ... +} +``` diff --git a/docs/api-reference/data-fetching/get-static-props.md b/docs/api-reference/data-fetching/get-static-props.md new file mode 100644 index 000000000000..7e04d1d9252e --- /dev/null +++ b/docs/api-reference/data-fetching/get-static-props.md @@ -0,0 +1,244 @@ +--- +description: API reference for `getStaticProps`. Learn how to use `getStaticProps` to generate static pages with Next.js. +--- + +# getStaticProps + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ----------------------------------------------------------------------------------------------------------------- | +| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | +| `v9.5.0` | Stable [Incremental Static Regeneration](https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration) | +| `v9.3.0` | `getStaticProps` introduced. | +| `v10.0.0` | `fallback: 'blocking'` return option added. | + +</details> + +Exporting a function called `getStaticProps` will pre-render a page at build time using the props returned from the function: + +```jsx +export async function getStaticProps(context) { + return { + props: {}, // will be passed to the page component as props + } +} +``` + +You can import modules in top-level scope for use in `getStaticProps`. Imports used will **not be bundled for the client-side**. This means you can write **server-side code directly in `getStaticProps`**, including fetching data from your database. + +## Context parameter + +The `context` parameter is an object containing the following keys: + +- `params` contains the route parameters for pages using [dynamic routes](/docs/routing/dynamic-routes.md). For example, if the page name is `[id].js` , then `params` will look like `{ id: ... }`. You should use this together with `getStaticPaths`, which we’ll explain later. +- `preview` is `true` if the page is in the [Preview Mode](/docs/advanced-features/preview-mode.md) and `undefined` otherwise. +- `previewData` contains the [preview](/docs/advanced-features/preview-mode.md) data set by `setPreviewData`. +- `locale` contains the active locale (if enabled). +- `locales` contains all supported locales (if enabled). +- `defaultLocale` contains the configured default locale (if enabled). + +## getStaticProps return values + +The `getStaticProps` function should return an object containing either `props`, `redirect`, or `notFound` followed by an **optional** `revalidate` property. + +### `props` + +The `props` object is a key-value pair, where each value is received by the page component. It should be a [serializable object](https://developer.mozilla.org/en-US/docs/Glossary/Serialization) so that any props passed, could be serialized with [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). + +```jsx +export async function getStaticProps(context) { + return { + props: { message: `Next.js is awesome` }, // will be passed to the page component as props + } +} +``` + +### `revalidate` + +The `revalidate` property is the amount in seconds after which a page re-generation can occur (defaults to `false` or no revalidation). + +```js +// This function gets called at build time on server-side. +// It may be called again, on a serverless function, if +// revalidation is enabled and a new request comes in +export async function getStaticProps() { + const res = await fetch('https://.../posts') + const posts = await res.json() + + return { + props: { + posts, + }, + // Next.js will attempt to re-generate the page: + // - When a request comes in + // - At most once every 10 seconds + revalidate: 10, // In seconds + } +} +``` + +Learn more about [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) + +### `notFound` + +The `notFound` boolean allows the page to return a `404` status and [404 Page](/docs/advanced-features/custom-error-page.md#404-page). With `notFound: true`, the page will return a `404` even if there was a successfully generated page before. This is meant to support use cases like user-generated content getting removed by its author. + +```js +export async function getStaticProps(context) { + const res = await fetch(`https://.../data`) + const data = await res.json() + + if (!data) { + return { + notFound: true, + } + } + + return { + props: { data }, // will be passed to the page component as props + } +} +``` + +> **Note**: `notFound` is not needed for [`fallback: false`](/docs/api-reference/data-fetching/get-static-paths#fallback-false) mode as only paths returned from `getStaticPaths` will be pre-rendered. + +### `redirect` + +The `redirect` object allows redirecting to internal or external resources. It should match the shape of `{ destination: string, permanent: boolean }`. + +In some rare cases, you might need to assign a custom status code for older `HTTP` clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, **but not both**. You can also set `basePath: false` similar to redirects in `next.config.js`. + +```js +export async function getStaticProps(context) { + const res = await fetch(`https://...`) + const data = await res.json() + + if (!data) { + return { + redirect: { + destination: '/', + permanent: false, + // statusCode: 301 + }, + } + } + + return { + props: { data }, // will be passed to the page component as props + } +} +``` + +If the redirects are known at build-time, they should be added in [`next.config.js`](/docs/api-reference/next.config.js/redirects.md) instead. + +## Reading files: Use `process.cwd()` + +Files can be read directly from the filesystem in `getStaticProps`. + +In order to do so you have to get the full path to a file. + +Since Next.js compiles your code into a separate directory you can't use `__dirname` as the path it will return will be different from the pages directory. + +Instead you can use `process.cwd()` which gives you the directory where Next.js is being executed. + +```jsx +import { promises as fs } from 'fs' +import path from 'path' + +// posts will be populated at build time by getStaticProps() +function Blog({ posts }) { + return ( + <ul> + {posts.map((post) => ( + <li> + <h3>{post.filename}</h3> + <p>{post.content}</p> + </li> + ))} + </ul> + ) +} + +// This function gets called at build time on server-side. +// It won't be called on client-side, so you can even do +// direct database queries. +export async function getStaticProps() { + const postsDirectory = path.join(process.cwd(), 'posts') + const filenames = await fs.readdir(postsDirectory) + + const posts = filenames.map(async (filename) => { + const filePath = path.join(postsDirectory, filename) + const fileContents = await fs.readFile(filePath, 'utf8') + + // Generally you would parse/transform the contents + // For example you can transform markdown to HTML here + + return { + filename, + content: fileContents, + } + }) + // By returning { props: { posts } }, the Blog component + // will receive `posts` as a prop at build time + return { + props: { + posts: await Promise.all(posts), + }, + } +} + +export default Blog +``` + +## getStaticProps with TypeScript + +You can use the `GetStaticProps` type from `next` to type the function: + +```ts +import { GetStaticProps } from 'next' + +export const getStaticProps: GetStaticProps = async (context) => { + // ... +} +``` + +If you want to get inferred typings for your props, you can use `InferGetStaticPropsType<typeof getStaticProps>`: + +```tsx +import { InferGetStaticPropsType } from 'next' + +type Post = { + author: string + content: string +} + +export const getStaticProps = async () => { + const res = await fetch('https://.../posts') + const posts: Post[] = await res.json() + + return { + props: { + posts, + }, + } +} + +function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) { + // will resolve posts to type Post[] +} + +export default Blog +``` + +## Related + +For more information on what to do next, we recommend the following sections: + +<div class="card"> + <a href="/docs/basic-features/data-fetching/overview.md"> + <b>Data Fetching:</b> + <small>Learn more about data fetching in Next.js.</small> + </a> +</div> diff --git a/docs/api-reference/data-fetching/getInitialProps.md b/docs/api-reference/data-fetching/getInitialProps.md deleted file mode 100644 index f46b34615f56..000000000000 --- a/docs/api-reference/data-fetching/getInitialProps.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -description: Enable Server-Side Rendering in a page and do initial data population with `getInitialProps`. ---- - -# getInitialProps - -> **Recommended: [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering)**. -> -> If you're using Next.js 9.3 or newer, we recommend that you use `getStaticProps` or `getServerSideProps` instead of `getInitialProps`. -> -> These new data fetching methods allow you to have a granular choice between static generation and server-side rendering. Learn more on the documentation for [Pages](/docs/basic-features/pages.md) and [Data Fetching](/docs/basic-features/data-fetching.md). - -`getInitialProps` enables [server-side rendering](/docs/basic-features/pages.md#server-side-rendering) in a page and allows you to do **initial data population**, it means sending the [page](/docs/basic-features/pages.md) with the data already populated from the server. This is especially useful for [SEO](https://en.wikipedia.org/wiki/Search_engine_optimization). - -> `getInitialProps` will disable [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md). - -`getInitialProps` is an [`async`](https://vercel.com/blog/async-and-await) function that can be added to any page as a [`static method`](https://javascript.info/static-properties-methods). Take a look at the following example: - -```jsx -function Page({ stars }) { - return <div>Next stars: {stars}</div> -} - -Page.getInitialProps = async (ctx) => { - const res = await fetch('https://api.github.com/repos/vercel/next.js') - const json = await res.json() - return { stars: json.stargazers_count } -} - -export default Page -``` - -Or using a class component: - -```jsx -import React from 'react' - -class Page extends React.Component { - static async getInitialProps(ctx) { - const res = await fetch('https://api.github.com/repos/vercel/next.js') - const json = await res.json() - return { stars: json.stargazers_count } - } - - render() { - return <div>Next stars: {this.props.stars}</div> - } -} - -export default Page -``` - -`getInitialProps` is used to asynchronously fetch some data, which then populates `props`. - -Data returned from `getInitialProps` is serialized when server rendering, similar to what [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) does. Make sure the returned object from `getInitialProps` is a plain `Object` and not using `Date`, `Map` or `Set`. - -For the initial page load, `getInitialProps` will run on the server only. `getInitialProps` will then run on the client when navigating to a different route via the [`next/link`](/docs/api-reference/next/link.md) component or by using [`next/router`](/docs/api-reference/next/router.md). However, if `getInitialProps` is used in a custom `_app.js`, and the page being navigated to implements `getServerSideProps`, then `getInitialProps` will run on the server. - -## Context Object - -`getInitialProps` receives a single argument called `context`, it's an object with the following properties: - -- `pathname` - Current route. That is the path of the page in `/pages` -- `query` - Query string section of URL parsed as an object -- `asPath` - `String` of the actual path (including the query) shown in the browser -- `req` - [HTTP request object](https://nodejs.org/api/http.html#http_class_http_incomingmessage 'Class: http.IncomingMessage HTTP | Node.js v14.8.0 Documentation') (server only) -- `res` - [HTTP response object](https://nodejs.org/api/http.html#http_class_http_serverresponse 'Class: http.ServerResponse HTTP | Node.js v14.8.0 Documentation') (server only) -- `err` - Error object if any error is encountered during the rendering - -## Caveats - -- `getInitialProps` can **not** be used in children components, only in the default export of every page -- If you are using server-side only modules inside `getInitialProps`, make sure to [import them properly](https://arunoda.me/blog/ssr-and-server-only-modules), otherwise it'll slow down your app - -## TypeScript - -If you're using TypeScript, you can use the `NextPage` type for function components: - -```jsx -import { NextPage } from 'next' - -interface Props { - userAgent?: string; -} - -const Page: NextPage<Props> = ({ userAgent }) => ( - <main>Your user agent: {userAgent}</main> -) - -Page.getInitialProps = async ({ req }) => { - const userAgent = req ? req.headers['user-agent'] : navigator.userAgent - return { userAgent } -} - -export default Page -``` - -And for `React.Component`, you can use `NextPageContext`: - -```jsx -import React from 'react' -import { NextPageContext } from 'next' - -interface Props { - userAgent?: string; -} - -export default class Page extends React.Component<Props> { - static async getInitialProps({ req }: NextPageContext) { - const userAgent = req ? req.headers['user-agent'] : navigator.userAgent - return { userAgent } - } - - render() { - const { userAgent } = this.props - return <main>Your user agent: {userAgent}</main> - } -} -``` - -## Related - -For more information on what to do next, we recommend the following sections: - -<div class="card"> - <a href="/docs/basic-features/data-fetching.md"> - <b>Data Fetching:</b> - <small>Learn more about data fetching in Next.js.</small> - </a> -</div> - -<div class="card"> - <a href="/docs/basic-features/pages.md"> - <b>Pages:</b> - <small>Learn more about what pages are in Next.js.</small> - </a> -</div> - -<div class="card"> - <a href="/docs/advanced-features/automatic-static-optimization.md"> - <b>Automatic Static Optimization:</b> - <small>Learn about how Nextjs automatically optimizes your pages.</small> - </a> -</div> diff --git a/docs/api-reference/edge-runtime.md b/docs/api-reference/edge-runtime.md new file mode 100644 index 000000000000..3c722513acac --- /dev/null +++ b/docs/api-reference/edge-runtime.md @@ -0,0 +1,99 @@ +--- +description: The Next.js Edge Runtime is based on standard Web APIs. Learn more about the supported APIs available. +--- + +# Edge Runtime + +The Next.js Edge Runtime is based on standard Web APIs, which is used by [Middleware](/docs/middleware.md). + +## Runtime APIs + +### Globals + +- [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) +- [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) +- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) + +### Base64 + +- [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/atob): Decodes a string of data which has been encoded using base-64 encoding +- [`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/btoa): Creates a base-64 encoded ASCII string from a string of binary data + +### Encoding + +- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder): Takes a stream of code points as input and emits a stream of bytes (UTF8) +- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder): Takes a stream of bytes as input and emit a stream of code points + +### Environment + +- `process.env`: Holds an object with all environment variables for both production and development in the exact same way as any other page or API in Next.js + +### Fetch + +The [Web Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) can be used from the runtime, enabling you to use Middleware as a proxy, or connect to external storage APIs + +A potential caveat to using the Fetch API in a Middleware function is latency. For example, if you have a Middleware function running a fetch request to New York, and a user accesses your site from London, the request will be resolved from the nearest Edge to the user (in this case, London), to the origin of the request, New York. There is a risk this could happen on every request, making your site slow to respond. When using the Fetch API, you _must_ make sure it does not run on every single request made. + +### Streams + +- [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream): Consists of a pair of streams: a writable stream known as its writable side, and a readable stream, known as its readable side. Writes to the writable side, result in new data being made available for reading from the readable side. Support for web streams is quite limited at the moment, although it is more extended in the development environment +- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream): A readable stream of byte data +- [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream): A standard abstraction for writing streaming data to a destination, known as a sink + +### Timers + +- [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval): Schedules a function to execute every time a given number of milliseconds elapses +- [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval): Cancels the repeated execution set using `setInterval()` +- [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout): Schedules a function to execute in a given amount of time +- [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout): Cancels the delayed execution set using `setTimeout()` + +### Web + +- [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers): A [WHATWG](https://whatwg.org/) implementation of the headers API +- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL): A WHATWG implementation of the URL API. +- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams): A WHATWG implementation of `URLSearchParams` + +### Crypto + +- [`Crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto): The `Crypto` interface represents basic cryptography features available in the current context +- [`crypto.randomUUID`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID): Lets you generate a v4 UUID using a cryptographically secure random number generator +- [`crypto.getRandomValues`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues): Lets you get cryptographically strong random values +- [`crypto.subtle`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle): A read-only property that returns a [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) which can then be used to perform low-level cryptographic operations + +### Logging + +- [`console.debug`](https://developer.mozilla.org/en-US/docs/Web/API/console/debug): Outputs a message to the console with the log level debug +- [`console.info`](https://developer.mozilla.org/en-US/docs/Web/API/console/info): Informative logging of information. You may use string substitution and additional arguments with this method +- [`console.clear`](https://developer.mozilla.org/en-US/docs/Web/API/console/clear): Clears the console +- [`console.dir`](https://developer.mozilla.org/en-US/docs/Web/API/console/dir): Displays an interactive listing of the properties of a specified JavaScript object +- [`console.count`](https://developer.mozilla.org/en-US/docs/Web/API/console/count): Log the number of times this line has been called with the given label +- [`console.time`](https://developer.mozilla.org/en-US/docs/Web/API/console/time): Starts a timer with a name specified as an input parameter + +## Unsupported APIs + +The Edge Runtime has some restrictions including: + +- Native Node.js APIs **are not supported**. For example, you can't read or write to the filesystem +- Node Modules _can_ be used, as long as they implement ES Modules and do not use any native Node.js APIs +- Calling `require` directly is **not allowed**. Use ES Modules instead + +The following JavaScript language features are disabled, and **will not work:** + +- [`eval`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval): Evaluates JavaScript code represented as a string +- [`new Function(evalString)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function): Creates a new function with the code provided as an argument + +## Related + +<div class="card"> + <a href="/docs/middleware.md"> + <b>Middleware</b> + <small>Run code before a request is completed.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/api-reference/next/server.md"> + <b>Middleware API Reference</b> + <small>Learn more about the supported APIs for Middleware.</small> + </a> +</div> diff --git a/docs/api-reference/next.config.js/build-indicator.md b/docs/api-reference/next.config.js/build-indicator.md new file mode 100644 index 000000000000..cccaa3d4bfad --- /dev/null +++ b/docs/api-reference/next.config.js/build-indicator.md @@ -0,0 +1,29 @@ +--- +description: In development mode, pages include an indicator to let you know if your new code it's being compiled. You can opt-out of it here. +--- + +# Build indicator + +When you edit your code, and Next.js is compiling the application, a compilation indicator appears in the bottom right corner of the page. + +> **Note:** This indicator is only present in development mode and will not appear when building and running the app in production mode. + +In some cases this indicator can be misplaced on your page, for example, when conflicting with a chat launcher. To change its position, open `next.config.js` and set the `buildActivityPosition` in the `devIndicators` object to `bottom-right` (default), `bottom-left`, `top-right` or `top-left`: + +```js +module.exports = { + devIndicators: { + buildActivityPosition: 'bottom-right', + }, +} +``` + +In some cases this indicator might not be useful for you. To remove it, open `next.config.js` and disable the `buildActivity` config in `devIndicators` object: + +```js +module.exports = { + devIndicators: { + buildActivity: false, + }, +} +``` diff --git a/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md b/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md index 20056521d6e7..0383f6ee6e6d 100644 --- a/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md +++ b/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md @@ -42,7 +42,7 @@ While `assetPrefix` covers requests to `_next/static`, it does not influence the - Files in the [public](/docs/basic-features/static-file-serving.md) folder; if you want to serve those assets over a CDN, you'll have to introduce the prefix yourself - `/_next/data/` requests for `getServerSideProps` pages. These requests will always be made against the main domain since they're not static. -- `/_next/data/` requests for `getStaticProps` pages. These requests will always be made against the main domain to support [Incremental Static Generation](/docs/basic-features/data-fetching.md#incremental-static-regeneration), even if you're not using it (for consistency). +- `/_next/data/` requests for `getStaticProps` pages. These requests will always be made against the main domain to support [Incremental Static Generation](/docs/basic-features/data-fetching/incremental-static-regeneration.md), even if you're not using it (for consistency). ## Related diff --git a/docs/api-reference/next.config.js/configuring-the-build-id.md b/docs/api-reference/next.config.js/configuring-the-build-id.md index 1d22438a7c20..aba9bd2cc8ad 100644 --- a/docs/api-reference/next.config.js/configuring-the-build-id.md +++ b/docs/api-reference/next.config.js/configuring-the-build-id.md @@ -4,7 +4,7 @@ description: Configure the build id, which is used to identify the current build # Configuring the Build ID -Next.js uses a constant id generated at build time to identify which version of your application is being served. This can cause problems in multi-server deployments when `next build` is ran on every server. In order to keep a static build id between builds you can provide your own build id. +Next.js uses a constant id generated at build time to identify which version of your application is being served. This can cause problems in multi-server deployments when `next build` is run on every server. In order to keep a consistent build id between builds you can provide your own build id. Open `next.config.js` and add the `generateBuildId` function: diff --git a/docs/api-reference/next.config.js/custom-page-extensions.md b/docs/api-reference/next.config.js/custom-page-extensions.md index dc11fdc6fac1..a3f0d6132424 100644 --- a/docs/api-reference/next.config.js/custom-page-extensions.md +++ b/docs/api-reference/next.config.js/custom-page-extensions.md @@ -16,7 +16,7 @@ module.exports = { > **Note**: The default value of `pageExtensions` is [`['tsx', 'ts', 'jsx', 'js']`](https://github.com/vercel/next.js/blob/f1dbc9260d48c7995f6c52f8fbcc65f08e627992/packages/next/server/config-shared.ts#L161). -> **Note**: configuring `pageExtensions` also affects `_document.js`, `_app.js` as well as files under `pages/api/`. For example, setting `pageExtensions: ['page.tsx', 'page.ts']` means the following files: `_document.tsx`, `_app.tsx`, `pages/users.tsx` and `pages/api/users.ts` will have to be renamed to `_document.page.tsx`, `_app.page.tsx`, `pages/users.page.tsx` and `pages/api/users.page.ts` respectively. +> **Note**: configuring `pageExtensions` also affects `_document.js`, `_app.js`, `_middleware.js` as well as files under `pages/api/`. For example, setting `pageExtensions: ['page.tsx', 'page.ts']` means the following files: `_document.tsx`, `_app.tsx`, `_middleware.ts`, `pages/users.tsx` and `pages/api/users.ts` will have to be renamed to `_document.page.tsx`, `_app.page.tsx`, `_middleware.page.ts`, `pages/users.page.tsx` and `pages/api/users.page.ts` respectively. ## Including non-page files in the `pages` directory @@ -32,7 +32,7 @@ module.exports = { Then rename your pages to have a file extension that includes `.page` (ex. rename `MyPage.tsx` to `MyPage.page.tsx`). -> **Note**: Make sure you also rename `_document.js`, `_app.js` as well as files under `pages/api/`. +> **Note**: Make sure you also rename `_document.js`, `_app.js`, `_middleware.js`, as well as files under `pages/api/`. Without this config, Next.js assumes every tsx/ts/jsx/js file in the `pages` directory is a page or API route, and may expose unintended routes vulnerable to denial of service attacks, or throw an error like the following when building the production bundle: diff --git a/docs/api-reference/next.config.js/exportPathMap.md b/docs/api-reference/next.config.js/exportPathMap.md index a5e4420ffa11..a1ef9f65021f 100644 --- a/docs/api-reference/next.config.js/exportPathMap.md +++ b/docs/api-reference/next.config.js/exportPathMap.md @@ -40,7 +40,7 @@ module.exports = { } ``` -Note: the `query` field in `exportPathMap` cannot be used with [automatically statically optimized pages](/docs/advanced-features/automatic-static-optimization) or [`getStaticProps` pages](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) as they are rendered to HTML files at build-time and additional query information cannot be provided during `next export`. +Note: the `query` field in `exportPathMap` cannot be used with [automatically statically optimized pages](/docs/advanced-features/automatic-static-optimization) or [`getStaticProps` pages](/docs/basic-features/data-fetching/get-static-props.md) as they are rendered to HTML files at build-time and additional query information cannot be provided during `next export`. The pages will then be exported as HTML files, for example, `/about` will become `/about.html`. diff --git a/docs/api-reference/next.config.js/headers.md b/docs/api-reference/next.config.js/headers.md index d64c01c676b7..5905a0860dd9 100644 --- a/docs/api-reference/next.config.js/headers.md +++ b/docs/api-reference/next.config.js/headers.md @@ -21,7 +21,7 @@ description: Add custom HTTP headers to your Next.js app. </details> -Headers allow you to set custom HTTP headers for an incoming request path. +Headers allow you to set custom HTTP headers on the response to an incoming request on a given path. To set custom HTTP headers you can use the `headers` key in `next.config.js`: @@ -50,7 +50,7 @@ module.exports = { `headers` is an async function that expects an array to be returned holding objects with `source` and `headers` properties: - `source` is the incoming request path pattern. -- `headers` is an array of header objects with the `key` and `value` properties. +- `headers` is an array of response header objects, with `key` and `value` properties. - `basePath`: `false` or `undefined` - if false the basePath won't be included when matching, can be used for external rewrites only. - `locale`: `false` or `undefined` - whether the locale should not be included when matching. - `has` is an array of [has objects](#header-cookie-and-query-matching) with the `type`, `key` and `value` properties. @@ -83,7 +83,7 @@ module.exports = { }, ], }, - ], + ] }, } ``` @@ -109,7 +109,7 @@ module.exports = { }, ], }, - ], + ] }, } ``` @@ -135,7 +135,7 @@ module.exports = { }, ], }, - ], + ] }, } ``` @@ -157,7 +157,7 @@ module.exports = { }, ], }, - ], + ] }, } ``` @@ -365,7 +365,7 @@ module.exports = { headers: [ { key: 'x-hello', - value: 'worlld', + value: 'world', }, ], }, @@ -376,7 +376,7 @@ module.exports = { ### Cache-Control -Cache-Control headers set in next.config.js will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](https://nextjs.org/docs/basic-features/pages#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) function. +Cache-Control headers set in next.config.js will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](https://nextjs.org/docs/basic-features/pages#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) function. ## Related diff --git a/docs/api-reference/next.config.js/introduction.md b/docs/api-reference/next.config.js/introduction.md index 02bf1c5904bb..121510630502 100644 --- a/docs/api-reference/next.config.js/introduction.md +++ b/docs/api-reference/next.config.js/introduction.md @@ -4,7 +4,7 @@ description: learn more about the configuration file used by Next.js to handle y # next.config.js -For custom advanced behavior of Next.js, you can create a `next.config.js` in the root of your project directory (next to `package.json`). +For custom advanced configuration of Next.js, you can create a `next.config.js` or `next.config.mjs` file in the root of your project directory (next to `package.json`). `next.config.js` is a regular Node.js module, not a JSON file. It gets used by the Next.js server and build phases, and it's not included in the browser build. @@ -21,6 +21,19 @@ const nextConfig = { module.exports = nextConfig ``` +If you need [ECMAScript modules](https://nodejs.org/api/esm.html), you can use `next.config.mjs`: + +```js +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + /* config options here */ +} + +export default nextConfig +``` + You can also use a function: ```js @@ -35,7 +48,21 @@ module.exports = (phase, { defaultConfig }) => { } ``` -`phase` is the current context in which the configuration is loaded. You can see the [available phases](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/constants.ts#L1-L4). Phases can be imported from `next/constants`: +Since Next.js 12.0.10, you can use an async function: + +```js +module.exports = async (phase, { defaultConfig }) => { + /** + * @type {import('next').NextConfig} + */ + const nextConfig = { + /* config options here */ + } + return nextConfig +} +``` + +`phase` is the current context in which the configuration is loaded. You can see the [available phases](https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/constants.ts#L1-L5). Phases can be imported from `next/constants`: ```js const { PHASE_DEVELOPMENT_SERVER } = require('next/constants') diff --git a/docs/api-reference/next.config.js/redirects.md b/docs/api-reference/next.config.js/redirects.md index 9d35b97c3450..30f7ac88c680 100644 --- a/docs/api-reference/next.config.js/redirects.md +++ b/docs/api-reference/next.config.js/redirects.md @@ -23,8 +23,6 @@ description: Add redirects to your Next.js app. Redirects allow you to redirect an incoming request path to a different destination path. -Redirects are only available on the Node.js environment and do not affect client-side routing. - To use Redirects you can use the `redirects` key in `next.config.js`: ```js @@ -45,7 +43,7 @@ module.exports = { - `source` is the incoming request path pattern. - `destination` is the path you want to route to. -- `permanent` if the redirect is permanent or not. +- `permanent` `true` or `false` - if `true` will use the 308 status code which instructs clients/search engines to cache the redirect forever, if `false` will use the 307 status code which is temporary and is not cached. - `basePath`: `false` or `undefined` - if false the basePath won't be included when matching, can be used for external rewrites only. - `locale`: `false` or `undefined` - whether the locale should not be included when matching. - `has` is an array of [has objects](#header-cookie-and-query-matching) with the `type`, `key` and `value` properties. @@ -201,13 +199,14 @@ module.exports = { // if the host is `example.com`, // this redirect will be applied { - source: '/:path((?!another-page$).*)',, + source: '/:path((?!another-page$).*)', has: [ { type: 'host', value: 'example.com', }, ], + permanent: false, destination: '/another-page', }, ] @@ -291,4 +290,4 @@ In some rare cases, you might need to assign a custom status code for older HTTP ## Other Redirects - Inside [API Routes](/docs/api-routes/response-helpers.md), you can use `res.redirect()`. -- Inside [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) and [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering), you can redirect specific pages at request-time. +- Inside [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) and [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md), you can redirect specific pages at request-time. diff --git a/docs/api-reference/next.config.js/rewrites.md b/docs/api-reference/next.config.js/rewrites.md index 46c58eb6035d..16ae2b533df8 100644 --- a/docs/api-reference/next.config.js/rewrites.md +++ b/docs/api-reference/next.config.js/rewrites.md @@ -50,7 +50,7 @@ Rewrites are applied to client-side routing, a `<Link href="/about">` will have - `locale`: `false` or `undefined` - whether the locale should not be included when matching. - `has` is an array of [has objects](#header-cookie-and-query-matching) with the `type`, `key` and `value` properties. -Rewrites are applied after checking the filesystem (pages and `/public` files) and before dynamic routes by default. This behavior can be changed by instead returning an object instead of an array from the `rewrites` function since `v10.1` of Next.js: +Rewrites are applied after checking the filesystem (pages and `/public` files) and before dynamic routes by default. This behavior can be changed by returning an object instead of an array from the `rewrites` function since `v10.1` of Next.js: ```js module.exports = { @@ -148,6 +148,8 @@ module.exports = { } ``` +Note: for static pages from the [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) or [prerendering](/docs/basic-features/data-fetching/get-static-props.md) params from rewrites will be parsed on the client after hydration and provided in the query. + ## Path Matching Path matches are allowed, for example `/blog/:slug` will match `/blog/hello-world` (no nested paths): diff --git a/docs/api-reference/next.config.js/url-imports.md b/docs/api-reference/next.config.js/url-imports.md new file mode 100644 index 000000000000..bb50c6ffe6b1 --- /dev/null +++ b/docs/api-reference/next.config.js/url-imports.md @@ -0,0 +1,94 @@ +--- +description: Configure Next.js to allow importing modules from external URLs (experimental). +--- + +# URL Imports + +URL imports are an experimental feature that allows you to import modules directly from external servers (instead of from the local disk). + +> **Warning**: This feature is experimental. Only use domains that you trust to download and execute on your machine. Please exercise +> discretion, and caution until the feature is flagged as stable. + +To opt-in, add the allowed URL prefixes inside `next.config.js`: + +```js +module.exports = { + experimental: { + urlImports: ['https://example.com/modules/'], + }, +} +``` + +Then, you can import modules directly from URLs: + +```js +import { a, b, c } from 'https://example.com/modules/some/module.js' +``` + +URL Imports can be used everywhere normal package imports can be used. + +## Security Model + +This feature is being designed with **security as the top priority**. To start, we added an experimental flag forcing you to explicitly allow the domains you accept URL imports from. We're working to take this further by limiting URL imports to execute in the browser sandbox using the [Edge Runtime](/docs/api-reference/edge-runtime.md). This runtime is used by [Middleware](/docs/middleware.md) as well as [Next.js Live](https://vercel.com/live). + +## Lockfile + +When using URL imports, Next.js will create a lockfile in the `next.lock` directory. +This directory is intended to be committed to Git and should **not be included** in your `.gitignore` file. + +- When running `next dev`, Next.js will download and add all newly discovered URL Imports to your lockfile +- When running `next build`, Next.js will use only the lockfile to build the application for production + +Typically, no network requests are needed and any outdated lockfile will cause the build to fail. +One exception is resources that respond with `Cache-Control: no-cache`. +These resources will have a `no-cache` entry in the lockfile and will always be fetched from the network on each build. + +## Examples + +### Skypack + +```js +import confetti from 'https://cdn.skypack.dev/canvas-confetti' +import { useEffect } from 'react' + +export default () => { + useEffect(() => { + confetti() + }) + return <p>Hello</p> +} +``` + +### Static Image Imports + +```js +import Image from 'next/image' +import logo from 'https://github.com/vercel/next.js/raw/canary/test/integration/production/public/vercel.png' + +export default () => ( + <div> + <Image src={logo} placeholder="blur" /> + </div> +) +``` + +### URLs in CSS + +```css +.className { + background: url('https://github.com/vercel/next.js/raw/canary/test/integration/production/public/vercel.png'); +} +``` + +### Asset Imports + +```js +import Image from 'next/image' + +const logo = new URL( + 'https://github.com/vercel/next.js/raw/canary/test/integration/production/public/vercel.png', + import.meta.url +) + +export default () => <div>{logo.pathname}</div> +``` diff --git a/docs/api-reference/next/head.md b/docs/api-reference/next/head.md index 63010ff4e064..96a6d14a5ea3 100644 --- a/docs/api-reference/next/head.md +++ b/docs/api-reference/next/head.md @@ -55,7 +55,7 @@ function IndexPage() { export default IndexPage ``` -In this case only the second `<meta property="og:title" />` is rendered. `meta` tags with duplicate `name` attributes are automatically handled. +In this case only the second `<meta property="og:title" />` is rendered. `meta` tags with duplicate `key` attributes are automatically handled. > The contents of `head` get cleared upon unmounting the component, so make sure each page completely defines what it needs in `head`, without making assumptions about what other pages added. diff --git a/docs/api-reference/next/image.md b/docs/api-reference/next/image.md index b093a8d1598b..830771ce3447 100644 --- a/docs/api-reference/next/image.md +++ b/docs/api-reference/next/image.md @@ -16,6 +16,8 @@ description: Enable Image Optimization with the built-in Image component. | Version | Changes | | --------- | ------------------------------------------------------------------------------------------------- | +| `v12.0.9` | `lazyRoot` prop added | +| `v12.0.0` | `formats` configuration added.<br/>AVIF support added.<br/>Wrapper `<div>` changed to `<span>`. | | `v11.1.0` | `onLoadingComplete` and `lazyBoundary` props added. | | `v11.0.0` | `src` prop support for static import.<br/>`placeholder` prop added.<br/>`blurDataURL` prop added. | | `v10.0.5` | `loader` prop added. | @@ -24,37 +26,7 @@ description: Enable Image Optimization with the built-in Image component. </details> -> Before moving forward, we recommend you to read -> [Image Optimization](/docs/basic-features/image-optimization.md) first. - -Image Optimization can be enabled via the `<Image />` component exported by -`next/image`. - -## Usage - -For an example, consider a project with the following files: - -- `pages/index.js` -- `public/me.png` - -We can serve an optimized image like so: - -```jsx -import Image from 'next/image' -import profilePic from '../public/me.png' - -function Home() { - return ( - <> - <h1>My Homepage</h1> - <Image src={profilePic} alt="Picture of the author" /> - <p>Welcome to my homepage!</p> - </> - ) -} - -export default Home -``` +> **Note: This is API documentation for the Image Component and Image Optimization. For a feature overview and usage information for images in Next.js, please see [Images](/docs/basic-features/image-optimization.md).** ## Required Props @@ -62,14 +34,14 @@ The `<Image />` component requires the following properties. ### src -Required and must be one of the following: +Must be one of the following: -1. A statically imported image file, as in the example code above, or +1. A [statically imported](/docs/basic-features/image-optimization.md#local-images) image file, or 2. A path string. This can be either an absolute external URL, - or an internal path depending on the [loader](#loader). + or an internal path depending on the [loader](#loader) prop or [loader configuration](#loader-configuration). When using an external URL, you must add it to -[domains](/docs/basic-features/image-optimization.md#domains) in +[domains](#domains) in `next.config.js`. ### width @@ -86,18 +58,18 @@ Required, except for statically imported images, or those with [`layout="fill"`] ## Optional Props -The `<Image />` component optionally accepts the following properties. +The `<Image />` component accepts a number of additional properties beyond those which are required. This section describes the most commonly-used properties of the Image component. Find details about more rarely-used properties in the [Advanced Props](#advanced-props) section. ### layout The layout behavior of the image as the viewport changes size. -| `layout` | Behavior | `srcSet` | `sizes` | -| --------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| `intrinsic` (default) | Scale *down* to fit width of container, up to image size | `1x`, `2x` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes)) | N/A | -| `fixed` | Sized to `width` and `height` exactly | `1x`, `2x` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes)) | N/A | -| `responsive` | Scale to fit width of container | `640w`, `750w`, ... `2048w`, `3840w` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes) and [deviceSizes](/docs/basic-features/image-optimization.md#device-sizes)) | `100vw` | -| `fill` | Grow in X and Y axes to fill container | `640w`, `750w`, ... `2048w`, `3840w` (based on [imageSizes](/docs/basic-features/image-optimization.md#image-sizes) and [deviceSizes](/docs/basic-features/image-optimization.md#device-sizes)) | `100vw` | +| `layout` | Behavior | `srcSet` | `sizes` | +| --------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------- | +| `intrinsic` (default) | Scale *down* to fit width of container, up to image size | `1x`, `2x` (based on [imageSizes](#image-sizes)) | N/A | +| `fixed` | Sized to `width` and `height` exactly | `1x`, `2x` (based on [imageSizes](#image-sizes)) | N/A | +| `responsive` | Scale to fit width of container | `640w`, `750w`, ... `2048w`, `3840w` (based on [imageSizes](#image-sizes) and [deviceSizes](#device-sizes)) | `100vw` | +| `fill` | Grow in both X and Y axes to fill container | `640w`, `750w`, ... `2048w`, `3840w` (based on [imageSizes](#image-sizes) and [deviceSizes](#device-sizes)) | `100vw` | - [Demo the `intrinsic` layout (default)](https://image-component.nextjs.gallery/layout-intrinsic) - When `intrinsic`, the image will scale the dimensions down for smaller viewports, but maintain the original dimensions for larger viewports. @@ -114,14 +86,16 @@ The layout behavior of the image as the viewport changes size. ### loader -A custom function used to resolve URLs. Defaults to [`images` object in `next.config.js`](/docs/basic-features/image-optimization.md#loader). +A custom function used to resolve URLs. Setting the loader as a prop on the Image component overrides the default loader defined in the [`images` section of `next.config.js`](#loader-configuration). -`loader` is a function returning a string, given the following parameters: +A `loader` is a function returning a URL string for the image, given the following parameters: - [`src`](#src) - [`width`](#width) - [`quality`](#quality) +Here is an example of using a custom loader with `next/image`: + ```js import Image from 'next/image' @@ -144,30 +118,34 @@ const MyImage = (props) => { ### sizes -A string mapping media queries to device sizes. Defaults to `100vw`. +A string that provides information about how wide the image will be at different breakpoints. Defaults to `100vw` (the full width of the screen) when using `layout="responsive"` or `layout="fill"`. -We recommend setting `sizes` when using `layout="responsive"` or `layout="fill"` and your image will **not** be the same width as the viewport. +If you are using `layout="fill"` or `layout="responsive"`, it's important to assign `sizes` for any image that takes up less than the full viewport width. + +For example, when the parent element will constrain the image to always be less than half the viewport width, use `sizes="50vw"`. Without `sizes`, the image will be sent at twice the necessary resolution, decreasing performance. + +If you are using `layout="intrinsic"` or `layout="fixed"`, then `sizes` is not needed because the upper bound width is constrained already. [Learn more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes). ### quality -The quality of the optimized image, an integer between `1` and `100` where `100` -is the best quality. Defaults to `75`. +The quality of the optimized image, an integer between `1` and `100` where `100` is the best quality. Defaults to `75`. ### priority When true, the image will be considered high priority and -[preload](https://web.dev/preload-responsive-images/). +[preload](https://web.dev/preload-responsive-images/). Lazy loading is automatically disabled for images using `priority`. + +You should use the `priority` property on any image detected as the [Largest Contentful Paint (LCP)](https://nextjs.org/learn/seo/web-performance/lcp) element. It may be appropriate to have multiple priority images, as different images may be the LCP element for different viewport sizes. -Should only be used when the image is visible above the fold. Defaults to -`false`. +Should only be used when the image is visible above the fold. Defaults to `false`. ### placeholder -A placeholder to use while the image is loading, possible values are `blur` or `empty`. Defaults to `empty`. +A placeholder to use while the image is loading. Possible values are `blur` or `empty`. Defaults to `empty`. -When `blur`, the [`blurDataURL`](#blurdataurl) property will be used as the placeholder. If `src` is an object from a static import and the imported image is jpg, png, or webp, then `blurDataURL` will automatically be populated. +When `blur`, the [`blurDataURL`](#blurdataurl) property will be used as the placeholder. If `src` is an object from a [static import](/docs/basic-features/image-optimization.md#local-images) and the imported image is `.jpg`, `.png`, `.webp`, or `.avif`, then `blurDataURL` will be automatically populated. For dynamic images, you must provide the [`blurDataURL`](#blurdataurl) property. Solutions such as [Plaiceholder](https://github.com/joe-bell/plaiceholder) can help with `base64` generation. @@ -177,23 +155,23 @@ Try it out: - [Demo the `blur` placeholder](https://image-component.nextjs.gallery/placeholder) - [Demo the shimmer effect with `blurDataURL` prop](https://image-component.nextjs.gallery/shimmer) +- [Demo the color effect with `blurDataURL` prop](https://image-component.nextjs.gallery/color) ## Advanced Props -In some cases, you may need more advanced usage. The `<Image />` component -optionally accepts the following advanced properties. +In some cases, you may need more advanced usage. The `<Image />` component optionally accepts the following advanced properties. ### objectFit -The image fit when using `layout="fill"`. +Defines how the image will fit into its parent container when using `layout="fill"`. -[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) +This value is passed to the [object-fit CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) for the `src` image. ### objectPosition -The image position when using `layout="fill"`. +Defines how the image is positioned within its parent element when using `layout="fill"`. -[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) +This value is passed to the [object-position CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) applied to the image. ### onLoadingComplete @@ -234,6 +212,7 @@ Try it out: - [Demo the default `blurDataURL` prop](https://image-component.nextjs.gallery/placeholder) - [Demo the shimmer effect with `blurDataURL` prop](https://image-component.nextjs.gallery/shimmer) +- [Demo the color effect with `blurDataURL` prop](https://image-component.nextjs.gallery/color) You can also [generate a solid color Data URL](https://png-pixel.com) to match the image. @@ -243,6 +222,12 @@ A string (with similar syntax to the margin property) that acts as the bounding [Learn more](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) +### lazyRoot + +A React [Ref](https://reactjs.org/docs/refs-and-the-dom.html) pointing to the Element which the [lazyBoundary](#lazyBoundary) calculates for the Intersection detection. Defaults to `null`, referring to the document viewport. + +[Learn more](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root) + ### unoptimized When true, the source image will be served as-is instead of changing quality, @@ -255,26 +240,166 @@ Other properties on the `<Image />` component will be passed to the underlying - `style`. Use `className` instead. - `srcSet`. Use - [Device Sizes](/docs/basic-features/image-optimization.md#device-sizes) + [Device Sizes](#device-sizes) instead. - `ref`. Use [`onLoadingComplete`](#onloadingcomplete) instead. - `decoding`. It is always `"async"`. -## Styling +## Configuration Options + +### Domains + +To protect your application from malicious users, you must define a list of image provider domains that you want to be served from the Next.js Image Optimization API. This is configured in with the `domains` property in your `next.config.js` file, as shown below: + +```js +module.exports = { + images: { + domains: ['assets.acme.com'], + }, +} +``` + +### Loader Configuration + +If you want to use a cloud provider to optimize images instead of using the Next.js built-in Image Optimization API, you can configure the `loader` and `path` prefix in your `next.config.js` file. This allows you to use relative URLs for the Image [`src`](#src) and automatically generate the correct absolute URL for your provider. + +```js +module.exports = { + images: { + loader: 'imgix', + path: 'https://example.com/myaccount/', + }, +} +``` + +### Built-in Loaders + +The following Image Optimization cloud providers are included: + +- Default: Works automatically with `next dev`, `next start`, or a custom server +- [Vercel](https://vercel.com): Works automatically when you deploy on Vercel, no configuration necessary. [Learn more](https://vercel.com/docs/next.js/image-optimization) +- [Imgix](https://www.imgix.com): `loader: 'imgix'` +- [Cloudinary](https://cloudinary.com): `loader: 'cloudinary'` +- [Akamai](https://www.akamai.com): `loader: 'akamai'` +- Custom: `loader: 'custom'` use a custom cloud provider by implementing the [`loader`](/docs/api-reference/next/image.md#loader) prop on the `next/image` component + +If you need a different provider, you can use the [`loader`](#loader) prop with `next/image`. + +> Images can not be optimized at build time using [`next export`](/docs/advanced-features/static-html-export.md), only on-demand. To use `next/image` with `next export`, you will need to use a different loader than the default. [Read more in the discussion.](https://github.com/vercel/next.js/discussions/19065) + +> The `next/image` component's default loader uses [`squoosh`](https://www.npmjs.com/package/@squoosh/lib) because it is quick to install and suitable for a development environment. When using `next start` in your production environment, it is strongly recommended that you install [`sharp`](https://www.npmjs.com/package/sharp) by running `yarn add sharp` in your project directory. This is not necessary for Vercel deployments, as `sharp` is installed automatically. + +## Advanced + +The following configuration is for advanced use cases and is usually not necessary. If you choose to configure the properties below, you will override any changes to the Next.js defaults in future updates. + +### Device Sizes + +If you know the expected device widths of your users, you can specify a list of device width breakpoints using the `deviceSizes` property in `next.config.js`. These widths are used when the [`next/image`](/docs/api-reference/next/image.md) component uses `layout="responsive"` or `layout="fill"` to ensure the correct image is served for user's device. + +If no configuration is provided, the default below is used. -`next/image` wraps the `img` element with other `div` elements to maintain the aspect ratio of the image and prevent [Cumulative Layout Shift](https://vercel.com/blog/core-web-vitals#cumulative-layout-shift). +```js +module.exports = { + images: { + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + }, +} +``` + +### Image Sizes + +You can specify a list of image widths using the `images.imageSizes` property in your `next.config.js` file. These widths are concatenated with the array of [device sizes](#device-sizes) to form the full array of sizes used to generate image [srcset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset)s. + +The reason there are two separate lists is that imageSizes is only used for images which provide a [`sizes`](#sizes) prop, which indicates that the image is less than the full width of the screen. **Therefore, the sizes in imageSizes should all be smaller than the smallest size in deviceSizes.** + +If no configuration is provided, the default below is used. + +```js +module.exports = { + images: { + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + }, +} +``` -To add styles to the underlying `img` element, pass the `className` prop to the `<Image />` component. Then, use Next.js' [built-in CSS support](/docs/basic-features/built-in-css-support.md) to add rules to that class. +### Acceptable Formats -**Note:** If using [`layout="fill"`](/docs/api-reference/next/image.md#layout), ensure the parent element uses `position: relative`. +The default [Image Optimization API](#loader-configuration) will automatically detect the browser's supported image formats via the request's `Accept` header. + +If the `Accept` head matches more than one of the configured formats, the first match in the array is used. Therefore, the array order matters. If there is no match, the Image Optimization API will fallback to the original image's format. + +If no configuration is provided, the default below is used. + +```js +module.exports = { + images: { + formats: ['image/webp'], + }, +} +``` + +You can enable AVIF support with the following configuration. + +```js +module.exports = { + images: { + formats: ['image/avif', 'image/webp'], + }, +} +``` + +> Note: AVIF generally takes 20% longer to encode but it compresses 20% smaller compared to WebP. This means that the first time an image is requested, it will typically be slower and then subsequent requests that are cached will be faster. + +## Caching Behavior + +The following describes the caching algorithm for the default [loader](#loader). For all other loaders, please refer to your cloud provider's documentation. + +Images are optimized dynamically upon request and stored in the `<distDir>/cache/images` directory. The optimized image file will be served for subsequent requests until the expiration is reached. When a request is made that matches a cached but expired file, the cached file is deleted before generating a new optimized image and caching the new file. + +The expiration (or rather Max Age) is defined by either the [`minimumCacheTTL`](#minimum-cache-ttl) configuration or the upstream server's `Cache-Control` header, whichever is larger. Specifically, the `max-age` value of the `Cache-Control` header is used. If both `s-maxage` and `max-age` are found, then `s-maxage` is preferred. + +- You can configure [`minimumCacheTTL`](#minimum-cache-ttl) to increase the cache duration when the upstream image does not include `Cache-Control` header or the value is very low. +- You can configure [`deviceSizes`](#device-sizes) and [`imageSizes`](#device-sizes) to reduce the total number of possible generated images. +- You can configure [formats](/docs/basic-features/image-optimization.md#acceptable-formats) to disable multiple formats in favor of a single image format. + +### Minimum Cache TTL + +You can configure the Time to Live (TTL) in seconds for cached optimized images. In many cases, it's better to use a [Static Image Import](/docs/basic-features/image-optimization.md#local-images) which will automatically hash the file contents and cache the image forever with a `Cache-Control` header of `immutable`. + +```js +module.exports = { + images: { + minimumCacheTTL: 60, + }, +} +``` + +If you need to add a `Cache-Control` header for the browser (not recommended), you can configure [`headers`](/docs/api-reference/next.config.js/headers) on the upstream image e.g. `/some-asset.jpg` not `/_next/image` itself. + +### Disable Static Imports + +The default behavior allows you to import static files such as `import icon from './icon.png` and then pass that to the `src` property. + +In some cases, you may wish to disable this feature if it conflicts with other plugins that expect the import to behave differently. + +You can disable static image imports inside your `next.config.js`: + +```js +module.exports = { + images: { + disableStaticImages: true, + }, +} +``` ## Related -For more information on what to do next, we recommend the following sections: +For an overview of the Image component features and usage guidelines, see: <div class="card"> <a href="/docs/basic-features/image-optimization.md"> - <b>Image Optimization</b> - <small>See how to configure domains and loaders.</small> + <b>Images</b> + <small>Learn how to display and optimize images with the Image component.</small> </a> </div> diff --git a/docs/api-reference/next/link.md b/docs/api-reference/next/link.md index a5272331968a..0525518af91f 100644 --- a/docs/api-reference/next/link.md +++ b/docs/api-reference/next/link.md @@ -55,12 +55,12 @@ export default Home `Link` accepts the following props: - `href` - The path or URL to navigate to. This is the only required prop -- `as` - Optional decorator for the path that will be shown in the browser URL bar. Before Next.js 9.5.3 this was used for dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked +- `as` - Optional decorator for the path that will be shown in the browser URL bar. Before Next.js 9.5.3 this was used for dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked. Note: when this path differs from the one provided in `href` the previous `href`/`as` behavior is used as shown in the [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes). - [`passHref`](#if-the-child-is-a-custom-component-that-wraps-an-a-tag) - Forces `Link` to send the `href` property to its child. Defaults to `false` -- `prefetch` - Prefetch the page in the background. Defaults to `true`. Any `<Link />` that is in the viewport (initially or through scroll) will be preloaded. Prefetch can be disabled by passing `prefetch={false}`. When `prefetch` is set to `false`, prefetching will still occur on hover. Pages using [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) will preload `JSON` files with the data for faster page transitions. Prefetching is only enabled in production. +- `prefetch` - Prefetch the page in the background. Defaults to `true`. Any `<Link />` that is in the viewport (initially or through scroll) will be preloaded. Prefetch can be disabled by passing `prefetch={false}`. When `prefetch` is set to `false`, prefetching will still occur on hover. Pages using [Static Generation](/docs/basic-features/data-fetching/get-static-props.md) will preload `JSON` files with the data for faster page transitions. Prefetching is only enabled in production. - [`replace`](#replace-the-url-instead-of-push) - Replace the current `history` state instead of adding a new url into the stack. Defaults to `false` - [`scroll`](#disable-scrolling-to-the-top-of-the-page) - Scroll to the top of the page after a navigation. Defaults to `true` -- [`shallow`](/docs/routing/shallow-routing.md) - Update the path of the current page without rerunning [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation), [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) or [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md). Defaults to `false` +- [`shallow`](/docs/routing/shallow-routing.md) - Update the path of the current page without rerunning [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md), [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md) or [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md). Defaults to `false` - `locale` - The active locale is automatically prepended. `locale` allows for providing a different locale. When `false` `href` has to include the locale as the default behavior is disabled. ## If the route has dynamic segments @@ -91,7 +91,7 @@ export default Posts ## If the child is a custom component that wraps an `<a>` tag -If the child of `Link` is a custom component that wraps an `<a>` tag, you must add `passHref` to `Link`. This is necessary if you’re using libraries like [styled-components](https://styled-components.com/). Without this, the `<a>` tag will not have the `href` attribute, which might hurt your site’s SEO. +If the child of `Link` is a custom component that wraps an `<a>` tag, you must add `passHref` to `Link`. This is necessary if you’re using libraries like [styled-components](https://styled-components.com/). Without this, the `<a>` tag will not have the `href` attribute, which hurts your site's accessibility and might affect SEO. If you're using [ESLint](/docs/basic-features/eslint.md#eslint-plugin), there is a built-in rule `next/link-passhref` to ensure correct usage of `passHref`. ```jsx import Link from 'next/link' @@ -117,9 +117,9 @@ export default NavLink - If you’re using [emotion](https://emotion.sh/)’s JSX pragma feature (`@jsx jsx`), you must use `passHref` even if you use an `<a>` tag directly. - The component should support `onClick` property to trigger navigation correctly -## If the child is a function component +## If the child is a functional component -If the child of `Link` is a function component, in addition to using `passHref`, you must wrap the component in [`React.forwardRef`](https://reactjs.org/docs/react-api.html#reactforwardref): +If the child of `Link` is a functional component, in addition to using `passHref`, you must wrap the component in [`React.forwardRef`](https://reactjs.org/docs/react-api.html#reactforwardref): ```jsx import Link from 'next/link' @@ -204,7 +204,7 @@ The default behavior of the `Link` component is to `push` a new URL into the `hi The default behavior of `Link` is to scroll to the top of the page. When there is a hash defined it will scroll to the specific id, like a normal `<a>` tag. To prevent scrolling to the top / hash `scroll={false}` can be added to `Link`: ```jsx -<Link href="/?counter=10" scroll={false}> +<Link href="/#hashid" scroll={false}> <a>Disables scrolling to the top</a> </Link> ``` diff --git a/docs/api-reference/next/router.md b/docs/api-reference/next/router.md index 60aa34f7f709..9c0e78dfd784 100644 --- a/docs/api-reference/next/router.md +++ b/docs/api-reference/next/router.md @@ -42,18 +42,18 @@ export default ActiveLink The following is the definition of the `router` object returned by both [`useRouter`](#useRouter) and [`withRouter`](#withRouter): - `pathname`: `String` - Current route. That is the path of the page in `/pages`, the configured `basePath` or `locale` is not included. -- `query`: `Object` - The query string parsed to an object. It will be an empty object during prerendering if the page doesn't have [data fetching requirements](/docs/basic-features/data-fetching.md). Defaults to `{}` +- `query`: `Object` - The query string parsed to an object. It will be an empty object during prerendering if the page doesn't have [data fetching requirements](/docs/basic-features/data-fetching/overview.md). Defaults to `{}` - `asPath`: `String` - The path (including the query) shown in the browser without the configured `basePath` or `locale`. -- `isFallback`: `boolean` - Whether the current page is in [fallback mode](/docs/basic-features/data-fetching.md#fallback-pages). +- `isFallback`: `boolean` - Whether the current page is in [fallback mode](/docs/api-reference/data-fetching/get-static-paths.md#fallback-pages). - `basePath`: `String` - The active [basePath](/docs/api-reference/next.config.js/basepath.md) (if enabled). - `locale`: `String` - The active locale (if enabled). - `locales`: `String[]` - All supported locales (if enabled). - `defaultLocale`: `String` - The current default locale (if enabled). - `domainLocales`: `Array<{domain, defaultLocale, locales}>` - Any configured domain locales. -- `isReady`: `boolean` - Whether the router fields are updated client-side and ready for use. Should only be used inside of `useEffect` methods and not for conditionally rendering on the server. +- `isReady`: `boolean` - Whether the router fields are updated client-side and ready for use. Should only be used inside of `useEffect` methods and not for conditionally rendering on the server. See related docs for use case with [automatically statically optimized pages](/docs/advanced-features/automatic-static-optimization.md) - `isPreview`: `boolean` - Whether the application is currently in [preview mode](/docs/advanced-features/preview-mode.md). -Additionally, the following methods are also included inside `router`: +The following methods are included inside `router`: ### router.push @@ -70,11 +70,11 @@ Handles client-side transitions, this method is useful for cases where [`next/li router.push(url, as, options) ``` -- `url` - The URL to navigate to -- `as` - Optional decorator for the URL that will be shown in the browser. Before Next.js 9.5.3 this was used for dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked +- `url`: `UrlObject | String` - The URL to navigate to (see [Node.JS URL module documentation](https://nodejs.org/api/url.html#legacy-urlobject) for `UrlObject` properties). +- `as`: `UrlObject | String` - Optional decorator for the path that will be shown in the browser URL bar. Before Next.js 9.5.3 this was used for dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked. Note: when this path differs from the one provided in `href` the previous `href`/`as` behavior is used as shown in the [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) - `options` - Optional object with the following configuration options: - `scroll` - Optional boolean, controls scrolling to the top of the page after navigation. Defaults to `true` - - [`shallow`](/docs/routing/shallow-routing.md): Update the path of the current page without rerunning [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation), [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) or [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md). Defaults to `false` + - [`shallow`](/docs/routing/shallow-routing.md): Update the path of the current page without rerunning [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md), [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md) or [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md). Defaults to `false` - `locale` - Optional string, indicates locale of the new page > You don't need to use `router.push` for external URLs. [window.location](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) is better suited for those cases. @@ -202,7 +202,7 @@ Prefetch pages for faster client-side transitions. This method is only useful fo router.prefetch(url, as) ``` -- `url` - The URL to prefetch, that is, a path with a matching page +- `url` - The URL to prefetch, including explicit routes (e.g. `/dashboard`) and dynamic routes (e.g. `/product/[id]`) - `as` - Optional decorator for `url`. Before Next.js 9.5.3 this was used to prefetch dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked #### Usage @@ -414,6 +414,53 @@ export default function MyApp({ Component, pageProps }) { } ``` +## Potential ESLint errors + +Certain methods accessible on the `router` object return a Promise. If you have the ESLint rule, [no-floating-promises](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-floating-promises.md) enabled, consider disabling it either globally, or for the affected line. + +If your application needs this rule, you should either `void` the promise – or use an `async` function, `await` the Promise, then void the function call. **This is not applicable when the method is called from inside an `onClick` handler**. + +The affected methods are: + +- `router.push` +- `router.replace` +- `router.prefetch` + +### Potential solutions + +```jsx +import { useEffect } from 'react' +import { useRouter } from 'next/router' + +// Here you would fetch and return the user +const useUser = () => ({ user: null, loading: false }) + +export default function Page() { + const { user, loading } = useUser() + const router = useRouter() + + useEffect(() => { + // disable the linting on the next line - This is the cleanest solution + // eslint-disable-next-line no-floating-promises + router.push('/login') + + // void the Promise returned by router.push + if (!(user || loading)) { + void router.push('/login') + } + // or use an async function, await the Promise, then void the function call + async function handleRouteChange() { + if (!(user || loading)) { + await router.push('/login') + } + } + void handleRouteChange() + }, [user, loading]) + + return <p>Redirecting...</p> +} +``` + ## withRouter If [`useRouter`](#useRouter) is not the best fit for you, `withRouter` can also add the same [`router` object](#router-object) to any component. diff --git a/docs/api-reference/next/script.md b/docs/api-reference/next/script.md new file mode 100644 index 000000000000..79c83341f887 --- /dev/null +++ b/docs/api-reference/next/script.md @@ -0,0 +1,72 @@ +--- +description: Optimize loading of third-party scripts with the built-in Script component. +--- + +# next/script + +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-tag-manager">Google Tag Manager</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-analytics">Google Analytics</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel">Facebook Pixel</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-clerk">Clerk</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-segment-analytics">Segment Analytics</a></li> + </ul> +</details> + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| --------- | ------------------------- | +| `v11.0.0` | `next/script` introduced. | + +</details> + +> **Note: This is API documentation for the Script Component. For a feature overview and usage information for scripts in Next.js, please see [Script Optimization](/docs/basic-features/script.md).** + +## Optional Props + +### src + +A path string specifying the URL of an external script. This can be either an absolute external URL or an internal path. + +### strategy + +The loading strategy of the script. + +| `strategy` | **Description** | +| ------------------- | ---------------------------------------------------------- | +| `beforeInteractive` | Load script before the page becomes interactive | +| `afterInteractive` | Load script immediately after the page becomes interactive | +| `lazyOnload` | Load script during browser idle time | + +### onLoad + +A method that returns additional JavaScript that should be executed after the script has finished loading. + +> **Note: `onLoad` can't be used with the `beforeInteractive` loading strategy.** + +The following is an example of how to use the `onLoad` property: + +```jsx +import { useState } from 'react' +import Script from 'next/script' + +export default function Home() { + const [stripe, setStripe] = useState(null) + + return ( + <> + <Script + id="stripe-js" + src="https://js.stripe.com/v3/" + onLoad={() => { + setStripe({ stripe: window.Stripe('pk_test_12345') }) + }} + /> + </> + ) +} +``` diff --git a/docs/api-reference/next/server.md b/docs/api-reference/next/server.md new file mode 100644 index 000000000000..0d42e941acb5 --- /dev/null +++ b/docs/api-reference/next/server.md @@ -0,0 +1,200 @@ +--- +description: Use Middleware to run code before a request is completed. +--- + +# next/server + +The `next/server` module provides several exports for server-only helpers, such as [Middleware](/docs/middleware.md). + +## NextMiddleware + +Middleware is created by using a `middleware` function that lives inside a `_middleware` file. The Middleware API is based upon the native [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request), [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. + +These native Web API objects are extended to give you more control over how you manipulate and configure a response, based on the incoming requests. + +The function signature is defined as follows: + +```ts +type NextMiddlewareResult = NextResponse | Response | null | undefined + +type NextMiddleware = ( + request: NextRequest, + event: NextFetchEvent +) => NextMiddlewareResult | Promise<NextMiddlewareResult> +``` + +It can be imported from `next/server` with the following: + +```ts +import type { NextMiddleware } from 'next/server' +``` + +The function can be a default export and as such, does **not** have to be named `middleware`. Though this is a convention. Also note that you only need to make the function `async` if you are running asynchronous code. + +## NextRequest + +The `NextRequest` object is an extension of the native [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) interface, with the following added methods and properties: + +- `cookies` - Has the cookies from the `Request` +- `nextUrl` - Includes an extended, parsed, URL object that gives you access to Next.js specific properties such as `pathname`, `basePath`, `trailingSlash` and `i18n` +- `ip` - Has the IP address of the `Request` +- `ua` - Has the user agent +- `geo` - (Optional) Has the geo location from the `Request`, provided by your hosting platform + +You can use the `NextRequest` object as a direct replacement for the native `Request` interface, giving you more control over how you manipulate the request. + +`NextRequest` is fully typed and can be imported from `next/server`. + +```ts +import type { NextRequest } from 'next/server' +``` + +## NextFetchEvent + +The `NextFetchEvent` object extends the native [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent) object, and includes the [`waitUntil()`](https://developer.mozilla.org/en-US/docs/Web/API/ExtendableEvent/waitUntil) method. + +The `waitUntil()` method can be used to prolong the execution of the function, after the response has been sent. In practice this means that you can send a response, then continue the function execution if you have other background work to make. + +An example of _why_ you would use `waitUntil()` is integrations with logging tools such as [Sentry](https://sentry.io) or [DataDog](https://www.datadoghq.com). After the response has been sent, you can send logs of response times, errors, API call durations or overall performance metrics. + +The `event` object is fully typed and can be imported from `next/server`. + +```ts +import type { NextFetchEvent } from 'next/server' +``` + +## NextResponse + +The `NextResponse` class extends the native [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) interface, with the following: + +### Public methods + +Public methods are available on an instance of the `NextResponse` class. Depending on your use case, you can create an instance and assign to a variable, then access the following public methods: + +- `cookies` - An object with the cookies in the `Response` +- `cookie()` - Set a cookie in the `Response` +- `clearCookie()` - Accepts a `cookie` and clears it + +```ts +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export function middleware(request: NextRequest) { + // create an instance of the class to access the public methods. This uses `next()`, + // you could use `redirect()` or `rewrite()` as well + let response = NextResponse.next() + // get the cookies from the request + let cookieFromRequest = request.cookies['my-cookie'] + // set the `cookie` + response.cookie('hello', 'world') + // set the `cookie` with options + const cookieWithOptions = response.cookie('hello', 'world', { + path: '/', + maxAge: 1000 * 60 * 60 * 24 * 7, + httpOnly: true, + sameSite: 'strict', + domain: 'example.com', + }) + // clear the `cookie` + response.clearCookie('hello') + + return response +} +``` + +### Static methods + +The following static methods are available on the `NextResponse` class directly: + +- `redirect()` - Returns a `NextResponse` with a redirect set +- `rewrite()` - Returns a `NextResponse` with a rewrite set +- `next()` - Returns a `NextResponse` that will continue the middleware chain +- `json()` - A convenience method to create a response that encodes the provided JSON data + +```ts +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export function middleware(req: NextRequest) { + // if the request is coming from New York, redirect to the home page + if (req.geo.city === 'New York') { + return NextResponse.redirect('/home') + // if the request is coming from London, rewrite to a special page + } else if (req.geo.city === 'London') { + return NextResponse.rewrite('/not-home') + } + + return NextResponse.json({ message: 'Hello World!' }) +} +``` + +All methods above return a `NextResponse` object that only takes effect if it's returned in the middleware function. + +`NextResponse` is fully typed and can be imported from `next/server`. + +```ts +import { NextResponse } from 'next/server' +``` + +### Setting the cookie before a redirect + +In order to set the `cookie` _before_ a redirect, you can create an instance of `NextResponse`, then access the `cookie` method on the instance, before returning the response. + +Note that there is a [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=696204) which means the entire redirect chain **must** be from the same origin, if they are from different origins, then the `cookie` might be missing until a refresh. + +```ts +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export function middleware(req: NextRequest) { + const res = NextResponse.redirect('/') // creates an actual instance + res.cookie('hello', 'world') // can be called on an instance + return res +} +``` + +### Why does redirect use 307 and 308? + +When using `redirect()` you may notice that the status codes used are `307` for a temporary redirect, and `308` for a permanent redirect. While traditionally a `302` was used for a temporary redirect, and a `301` for a permanent redirect, many browsers changed the request method of the redirect, from a `POST` to `GET` request when using a `302`, regardless of the origins request method. + +Taking the following example of a redirect from `/users` to `/people`, if you make a `POST` request to `/users` to create a new user, and are conforming to a `302` temporary redirect, the request method will be changed from a `POST` to a `GET` request. This doesn't make sense, as to create a new user, you should be making a `POST` request to `/people`, and not a `GET` request. + +The introduction of the `307` status code means that the request method is preserved as `POST`. + +- `302` - Temporary redirect, will change the request method from `POST` to `GET` +- `307` - Temporary redirect, will preserve the request method as `POST` + +The `redirect()` method uses a `307` by default, instead of a `302` temporary redirect, meaning your requests will _always_ be preserved as `POST` requests. + +### How do I access Environment Variables? + +`process.env` can be used to access [Environment Variables](/docs/basic-features/environment-variables.md) from Middleware. These are evaluated at build time, so only environment variables _actually_ used will be included. + +Any variables in `process.env` must be accessed directly, and **cannot** be destructured: + +```ts +// Accessed directly, and not destructured works. process.env.NODE_ENV is `"development"` or `"production"` +console.log(process.env.NODE_ENV) +// This will not work +const { NODE_ENV } = process.env +// NODE_ENV is `undefined` +console.log(NODE_ENV) +// process.env is `{}` +console.log(process.env) +``` + +## Related + +<div class="card"> + <a href="/docs/api-reference/edge-runtime.md"> + <b>Edge Runtime</b> + <small>Learn more about the supported Web APIs available.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/middleware.md"> + <b>Middleware</b> + <small>Run code before a request is completed.</small> + </a> +</div> diff --git a/docs/api-routes/api-middlewares.md b/docs/api-routes/api-middlewares.md index 5710da1593df..40b15ac2f718 100644 --- a/docs/api-routes/api-middlewares.md +++ b/docs/api-routes/api-middlewares.md @@ -34,7 +34,9 @@ export const config = { The `api` object includes all configs available for API routes. -`bodyParser` Enables body parsing, you can disable it if you want to consume it as a `Stream`: +`bodyParser` is automatically enabled. If you want to consume the body as a `Stream` or with [`raw-body`](https://www.npmjs.com/package/raw-body), you can set this to `false`. + +One use case for disabling the automatic `bodyParsing` is to allow you to verify the raw body of a **webhook** request, for example [from GitHub](https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks#validating-payloads-from-github). ```js export const config = { @@ -145,7 +147,7 @@ export const setCookie = ( options.maxAge /= 1000 } - res.setHeader('Set-Cookie', serialize(name, String(stringValue), options)) + res.setHeader('Set-Cookie', serialize(name, stringValue, options)) } // pages/api/cookies.ts diff --git a/docs/api-routes/introduction.md b/docs/api-routes/introduction.md index 4274f7d0f7b2..3177caa5a68b 100644 --- a/docs/api-routes/introduction.md +++ b/docs/api-routes/introduction.md @@ -29,8 +29,8 @@ export default function handler(req, res) { For an API route to work, you need to export a function as default (a.k.a **request handler**), which then receives the following parameters: -- `req`: An instance of [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage), plus some [pre-built middlewares](/docs/api-routes/api-middlewares.md) -- `res`: An instance of [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse), plus some [helper functions](/docs/api-routes/response-helpers.md) +- `req`: An instance of [http.IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage), plus some [pre-built middlewares](/docs/api-routes/api-middlewares.md) +- `res`: An instance of [http.ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse), plus some [helper functions](/docs/api-routes/response-helpers.md) To handle different HTTP methods in an API route, you can use `req.method` in your request handler, like so: diff --git a/docs/api-routes/response-helpers.md b/docs/api-routes/response-helpers.md index 0418b2362e44..41673745b259 100644 --- a/docs/api-routes/response-helpers.md +++ b/docs/api-routes/response-helpers.md @@ -4,27 +4,102 @@ description: API Routes include a set of Express.js-like methods for the respons # Response Helpers -<details open> - <summary><b>Examples</b></summary> - <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/api-routes">Basic API Routes</a></li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/api-routes-rest">API Routes with REST</a></li> - </ul> -</details> +The [Server Response object](https://nodejs.org/api/http.html#http_class_http_serverresponse), (often abbreviated as `res`) includes a set of Express.js-like helper methods to improve the developer experience and increase the speed of creating new API endpoints. -The response (`res`) includes a set of Express.js-like methods to improve the developer experience and increase the speed of creating new API endpoints, take a look at the following example: +The included helpers are: + +- `res.status(code)` - A function to set the status code. `code` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) +- `res.json(body)` - Sends a JSON response. `body` must be a [serializable object](https://developer.mozilla.org/en-US/docs/Glossary/Serialization) +- `res.send(body)` - Sends the HTTP response. `body` can be a `string`, an `object` or a `Buffer` +- `res.redirect([status,] path)` - Redirects to a specified path or URL. `status` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). If not specified, `status` defaults to "307" "Temporary redirect". + +## Setting the status code of a response + +When sending a response back to the client, you can set the status code of the response. + +The following example sets the status code of the response to `200` (`OK`) and returns a `message` property with the value of `Hello from Next.js!` as a JSON response: ```js export default function handler(req, res) { - res.status(200).json({ name: 'Next.js' }) + res.status(200).json({ message: 'Hello from Next.js!' }) } ``` -The included helpers are: +## Sending a JSON response -- `res.status(code)` - A function to set the status code. `code` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) -- `res.json(body)` - Sends a JSON response. `body` must be a [serialiazable object](https://developer.mozilla.org/en-US/docs/Glossary/Serialization) -- `res.send(body)` - Sends the HTTP response. `body` can be a `string`, an `object` or a `Buffer` -- `res.redirect([status,] path)` - Redirects to a specified path or URL. `status` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). If not specified, `status` defaults to "307" "Temporary redirect". +When sending a response back to the client you can send a JSON response, this must be a [serializable object](https://developer.mozilla.org/en-US/docs/Glossary/Serialization). +In a real world application you might want to let the client know the status of the request depending on the result of the requested endpoint. + +The following example sends a JSON response with the status code `200` (`OK`) and the result of the async operation. It's contained in a try catch block to handle any errors that may occur, with the appropriate status code and error message caught and sent back to the client: + +```js +export default async function handler(req, res) { + try { + const result = await someAsyncOperation() + res.status(200).json({ result }) + } catch (err) { + res.status(500).json({ error: 'failed to load data' }) + } +} +``` + +## Sending a HTTP response + +Sending an HTTP response works the same way as when sending a JSON response. The only difference is that the response body can be a `string`, an `object` or a `Buffer`. + +The following example sends a HTTP response with the status code `200` (`OK`) and the result of the async operation. + +```js +export default async function handler(req, res) { + try { + const result = await someAsyncOperation() + res.status(200).send({ result }) + } catch (err) { + res.status(500).send({ error: 'failed to fetch data' }) + } +} +``` + +## Redirects to a specified path or URL + +Taking a form as an example, you may want to redirect your client to a specified path or URL once they have submitted the form. + +The following example redirects the client to the `/` path if the form is successfully submitted: + +```js +export default async function handler(req, res) { + const { name, message } = req.body + try { + await handleFormInputAsync({ name, message }) + res.redirect(307, '/') + } catch (err) { + res.status(500).send({ error: 'failed to fetch data' }) + } +} +``` + +## Adding TypeScript types + +You can make your response handlers more type-safe by importing the `NextApiRequest` and `NextApiResponse` types from `next`, in addition to those, you can also type your response data: + +```ts +import type { NextApiRequest, NextApiResponse } from 'next' + +type ResponseData = { + message: string +} + +export default function handler( + req: NextApiRequest, + res: NextApiResponse<ResponseData> +) { + res.status(200).json({ message: 'Hello from Next.js!' }) +} +``` + +To view more examples using types, check out the [TypeScript documentation](/docs/basic-features/typescript.md#api-routes). + +If you prefer to view your examples within a real projects structure you can checkout our examples repository: -To view an example using types, check out the [TypeScript documentation](/docs/basic-features/typescript.md#api-routes). +- [Basic API Routes](https://github.com/vercel/next.js/tree/canary/examples/api-routes) +- [API Routes with REST](https://github.com/vercel/next.js/tree/canary/examples/api-routes-rest) diff --git a/docs/authentication.md b/docs/authentication.md index f0170d95cdc1..c0d1140d1ef9 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -8,14 +8,14 @@ Authentication verifies who a user is, while authorization controls what a user ## Authentication Patterns -The first step to identifying which authentication pattern you need is understanding the [data-fetching strategy](/docs/basic-features/data-fetching.md) you want. We can then determine which authentication providers support this strategy. There are two main patterns: +The first step to identifying which authentication pattern you need is understanding the [data-fetching strategy](/docs/basic-features/data-fetching/overview.md) you want. We can then determine which authentication providers support this strategy. There are two main patterns: - Use [static generation](/docs/basic-features/pages.md#static-generation-recommended) to server-render a loading state, followed by fetching user data client-side. - Fetch user data [server-side](/docs/basic-features/pages.md#server-side-rendering) to eliminate a flash of unauthenticated content. ### Authenticating Statically Generated Pages -Next.js automatically determines that a page is static if there are no blocking data requirements. This means the absence of [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) and `getInitialProps` in the page. Instead, your page can render a loading state from the server, followed by fetching the user client-side. +Next.js automatically determines that a page is static if there are no blocking data requirements. This means the absence of [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md) and `getInitialProps` in the page. Instead, your page can render a loading state from the server, followed by fetching the user client-side. One advantage of this pattern is it allows pages to be served from a global CDN and preloaded using [`next/link`](/docs/api-reference/next/link.md). In practice, this results in a faster TTI ([Time to Interactive](https://web.dev/interactive/)). @@ -48,11 +48,11 @@ const Profile = () => { export default Profile ``` -You can view this [example in action](https://next-with-iron-session.vercel.app/). Check out the [`with-iron-session`](https://github.com/vercel/next.js/tree/canary/examples/with-iron-session) example to see how it works. +You can view this [example in action](https://iron-session-example.vercel.app/). Check out the [`with-iron-session`](https://github.com/vercel/next.js/tree/canary/examples/with-iron-session) example to see how it works. ### Authenticating Server-Rendered Pages -If you export an `async` function called [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) from a page, Next.js will pre-render this page on each request using the data returned by `getServerSideProps`. +If you export an `async` function called [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md) from a page, Next.js will pre-render this page on each request using the data returned by `getServerSideProps`. ```jsx export async function getServerSideProps(context) { @@ -62,7 +62,7 @@ export async function getServerSideProps(context) { } ``` -Let's transform the profile example to use [server-side rendering](/docs/basic-features/pages#server-side-rendering). If there's a session, return `user` as a prop to the `Profile` component in the page. Notice there is not a loading skeleton in [this example](https://next-with-iron-session.vercel.app/). +Let's transform the profile example to use [server-side rendering](/docs/basic-features/pages#server-side-rendering). If there's a session, return `user` as a prop to the `Profile` component in the page. Notice there is not a loading skeleton in [this example](https://iron-session-example.vercel.app/). ```jsx // pages/profile.js @@ -71,8 +71,7 @@ import withSession from '../lib/session' import Layout from '../components/Layout' export const getServerSideProps = withSession(async function ({ req, res }) { - // Get the user's session based on the request - const user = req.session.get('user') + const { user } = req.session if (!user) { return { @@ -119,7 +118,7 @@ Now that we've discussed authentication patterns, let's look at specific provide If you have an existing database with user data, you'll likely want to utilize an open-source solution that's provider agnostic. -- If you want a low-level, encrypted, and stateless session utility use [`next-iron-session`](https://github.com/vercel/next.js/tree/canary/examples/with-iron-session). +- If you want a low-level, encrypted, and stateless session utility use [`iron-session`](https://github.com/vercel/next.js/tree/canary/examples/with-iron-session). - If you want a full-featured authentication system with built-in providers (Google, Facebook, GitHub…), JWT, JWE, email/password, magic links and more… use [`next-auth`](https://github.com/nextauthjs/next-auth-example). Both of these libraries support either authentication pattern. If you're interested in [Passport](http://www.passportjs.org/), we also have examples for it using secure and encrypted cookies: @@ -135,12 +134,14 @@ To see examples with other authentication providers, check out the [examples fol <summary><b>Examples</b></summary> <ul> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-firebase-authentication">with-firebase-authentication</a></li> + <li><a href="https://github.com/vercel/examples/tree/main/solutions/auth-with-ory">auth-with-ory</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-magic">with-magic</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/auth0">auth0</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-supabase-auth-realtime-db">with-supabase-auth-realtime-db</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-userbase">with-userbase</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-supertokens">with-supertokens</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-nhost-auth-realtime-graphql">with-nhost-auth-realtime-graphql</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-clerk">with-clerk</a></li> </ul> </details> @@ -156,7 +157,7 @@ For more information on what to do next, we recommend the following sections: </div> <div class="card"> - <a href="/docs/basic-features/data-fetching.md"> + <a href="/docs/basic-features/data-fetching/overview.md"> <b>Data Fetching:</b> <small>Learn more about data fetching in Next.js.</small> </a> diff --git a/docs/basic-features/built-in-css-support.md b/docs/basic-features/built-in-css-support.md index b160a2340484..822e64599c58 100644 --- a/docs/basic-features/built-in-css-support.md +++ b/docs/basic-features/built-in-css-support.md @@ -181,6 +181,34 @@ module.exports = { } ``` +### Sass Variables + +Next.js supports Sass variables exported from CSS Module files. + +For example, using the exported `primaryColor` Sass variable: + +```scss +/* variables.module.scss */ +$primary-color: #64FF00 + +:export { + primaryColor: $primary-color +} +``` + +```js +// pages/_app.js +import variables from '../styles/variables.module.scss' + +export default function MyApp({ Component, pageProps }) { + return ( + <Layout color={variables.primaryColor}> + <Component {...pageProps} /> + </Layout> + ) +} +``` + ## CSS-in-JS <details> @@ -192,10 +220,10 @@ module.exports = { <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-linaria">Linaria</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss-emotion">Tailwind CSS + Emotion</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-styletron">Styletron</a></li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-glamor">Glamor</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-cxs">Cxs</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-aphrodite">Aphrodite</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-fela">Fela</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-stitches">Stitches</a></li> </ul> </details> diff --git a/docs/basic-features/data-fetching.md b/docs/basic-features/data-fetching.md deleted file mode 100644 index 2eb8dd943e82..000000000000 --- a/docs/basic-features/data-fetching.md +++ /dev/null @@ -1,864 +0,0 @@ ---- -description: 'Next.js has 2 pre-rendering modes: Static Generation and Server-side rendering. Learn how they work here.' ---- - -# Data Fetching - -> This document is for Next.js versions 9.3 and up. If you’re using older versions of Next.js, refer to our [previous documentation](https://nextjs.org/docs/tag/v9.2.2/basic-features/data-fetching). - -<details open> - <summary><b>Examples</b></summary> - <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-wordpress">WordPress Example</a> (<a href="https://next-blog-wordpress.vercel.app">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter">Blog Starter using markdown files</a> (<a href="https://next-blog-starter.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-strapi">Strapi Example</a> (<a href="https://next-blog-strapi.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prepr">Prepr Example</a> (<a href="https://next-blog-prepr.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-agilitycms">Agility CMS Example</a> (<a href="https://next-blog-agilitycms.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic">Cosmic Example</a> (<a href="https://next-blog-cosmic.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-buttercms">ButterCMS Example</a> (<a href="https://next-blog-buttercms.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app/">Demo</a>)</li> - <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> - </ul> -</details> - -In the [Pages documentation](/docs/basic-features/pages.md), we’ve explained that Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. In this page, we’ll talk in depth about data fetching strategies for each case. We recommend you to [read through the Pages documentation](/docs/basic-features/pages.md) first if you haven’t done so. - -We’ll talk about the three unique Next.js functions you can use to fetch data for pre-rendering: - -- [`getStaticProps`](#getstaticprops-static-generation) (Static Generation): Fetch data at **build time**. -- [`getStaticPaths`](#getstaticpaths-static-generation) (Static Generation): Specify [dynamic routes](/docs/routing/dynamic-routes.md) to pre-render pages based on data. -- [`getServerSideProps`](#getserversideprops-server-side-rendering) (Server-side Rendering): Fetch data on **each request**. - -In addition, we’ll talk briefly about how to fetch data on the client side. - -## `getStaticProps` (Static Generation) - -<details> - <summary><b>Version History</b></summary> - -| Version | Changes | -| --------- | ----------------------------------------------------------------------------------------------------------------- | -| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | -| `v9.5.0` | Stable [Incremental Static Regeneration](https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration) | -| `v9.3.0` | `getStaticProps` introduced. | - -</details> - -If you export an `async` function called `getStaticProps` from a page, Next.js will pre-render this page at build time using the props returned by `getStaticProps`. - -```jsx -export async function getStaticProps(context) { - return { - props: {}, // will be passed to the page component as props - } -} -``` - -The `context` parameter is an object containing the following keys: - -- `params` contains the route parameters for pages using dynamic routes. For example, if the page name is `[id].js` , then `params` will look like `{ id: ... }`. To learn more, take a look at the [Dynamic Routing documentation](/docs/routing/dynamic-routes.md). You should use this together with `getStaticPaths`, which we’ll explain later. -- `preview` is `true` if the page is in the preview mode and `undefined` otherwise. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). -- `previewData` contains the preview data set by `setPreviewData`. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). -- `locale` contains the active locale (if enabled). -- `locales` contains all supported locales (if enabled). -- `defaultLocale` contains the configured default locale (if enabled). - -`getStaticProps` should return an object with: - -- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) -- `revalidate` - An **optional** amount in seconds after which a page re-generation can occur. Defaults to `false`. When `revalidate` is `false` it means that there is no revalidation, so the page will be cached as built until your next build. More on [Incremental Static Regeneration](#incremental-static-regeneration) -- `notFound` - An **optional** boolean value to allow the page to return a 404 status and page. Below is an example of how it works: - - ```js - export async function getStaticProps(context) { - const res = await fetch(`https://.../data`) - const data = await res.json() - - if (!data) { - return { - notFound: true, - } - } - - return { - props: { data }, // will be passed to the page component as props - } - } - ``` - - > **Note**: `notFound` is not needed for [`fallback: false`](#fallback-false) mode as only paths returned from `getStaticPaths` will be pre-rendered. - - > **Note**: With `notFound: true` the page will return a 404 even if there was a successfully generated page before. This is meant to support use-cases like user generated content getting removed by its author. - -- `redirect` - An **optional** redirect value to allow redirecting to internal and external resources. It should match the shape of `{ destination: string, permanent: boolean }`. In some rare cases, you might need to assign a custom status code for older HTTP Clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both. Below is an example of how it works: - - ```js - export async function getStaticProps(context) { - const res = await fetch(`https://...`) - const data = await res.json() - - if (!data) { - return { - redirect: { - destination: '/', - permanent: false, - }, - } - } - - return { - props: { data }, // will be passed to the page component as props - } - } - ``` - - > **Note**: Redirecting at build-time is currently not allowed and if the redirects are known at build-time they should be added in [`next.config.js`](/docs/api-reference/next.config.js/redirects.md). - -> **Note**: You can import modules in top-level scope for use in `getStaticProps`. -> Imports used in `getStaticProps` will [not be bundled for the client-side](#write-server-side-code-directly). -> -> This means you can write **server-side code directly in `getStaticProps`**. -> This includes reading from the filesystem or a database. - -> **Note**: You should not use [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to -> call an API route in `getStaticProps`. -> Instead, directly import the logic used inside your API route. -> You may need to slightly refactor your code for this approach. -> -> Fetching from an external API is fine! - -### Example - -Here’s an example which uses `getStaticProps` to fetch a list of blog posts from a CMS (content management system). This example is also in the [Pages documentation](/docs/basic-features/pages.md). - -```jsx -// posts will be populated at build time by getStaticProps() -function Blog({ posts }) { - return ( - <ul> - {posts.map((post) => ( - <li>{post.title}</li> - ))} - </ul> - ) -} - -// This function gets called at build time on server-side. -// It won't be called on client-side, so you can even do -// direct database queries. See the "Technical details" section. -export async function getStaticProps() { - // Call an external API endpoint to get posts. - // You can use any data fetching library - const res = await fetch('https://.../posts') - const posts = await res.json() - - // By returning { props: { posts } }, the Blog component - // will receive `posts` as a prop at build time - return { - props: { - posts, - }, - } -} - -export default Blog -``` - -### When should I use `getStaticProps`? - -You should use `getStaticProps` if: - -- The data required to render the page is available at build time ahead of a user’s request. -- The data comes from a headless CMS. -- The data can be publicly cached (not user-specific). -- The page must be pre-rendered (for SEO) and be very fast — `getStaticProps` generates HTML and JSON files, both of which can be cached by a CDN for performance. - -### TypeScript: Use `GetStaticProps` - -For TypeScript, you can use the `GetStaticProps` type from `next`: - -```ts -import { GetStaticProps } from 'next' - -export const getStaticProps: GetStaticProps = async (context) => { - // ... -} -``` - -If you want to get inferred typings for your props, you can use `InferGetStaticPropsType<typeof getStaticProps>`, like this: - -```tsx -import { InferGetStaticPropsType } from 'next' - -type Post = { - author: string - content: string -} - -export const getStaticProps = async () => { - const res = await fetch('https://.../posts') - const posts: Post[] = await res.json() - - return { - props: { - posts, - }, - } -} - -function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) { - // will resolve posts to type Post[] -} - -export default Blog -``` - -### Incremental Static Regeneration - -<details open> - <summary><b>Examples</b></summary> - <ul> - <li><a href="https://nextjs.org/commerce">Next.js Commerce</a></li> - <li><a href="https://reactions-demo.vercel.app/">GitHub Reactions Demo</a></li> - <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> - </ul> -</details> - -<details> - <summary><b>Version History</b></summary> - -| Version | Changes | -| -------- | ---------------- | -| `v9.5.0` | Base Path added. | - -</details> - -Next.js allows you to create or update static pages _after_ you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, **without needing to rebuild the entire site**. With ISR, you can retain the benefits of static while scaling to millions of pages. - -Consider our previous [`getStaticProps` example](#simple-example), but now with Incremental Static Regeneration enabled through the `revalidate` property: - -```jsx -function Blog({ posts }) { - return ( - <ul> - {posts.map((post) => ( - <li>{post.title}</li> - ))} - </ul> - ) -} - -// This function gets called at build time on server-side. -// It may be called again, on a serverless function, if -// revalidation is enabled and a new request comes in -export async function getStaticProps() { - const res = await fetch('https://.../posts') - const posts = await res.json() - - return { - props: { - posts, - }, - // Next.js will attempt to re-generate the page: - // - When a request comes in - // - At most once every 10 seconds - revalidate: 10, // In seconds - } -} - -// This function gets called at build time on server-side. -// It may be called again, on a serverless function, if -// the path has not been generated. -export async function getStaticPaths() { - const res = await fetch('https://.../posts') - const posts = await res.json() - - // Get the paths we want to pre-render based on posts - const paths = posts.map((post) => ({ - params: { id: post.id }, - })) - - // We'll pre-render only these paths at build time. - // { fallback: blocking } will server-render pages - // on-demand if the path doesn't exist. - return { paths, fallback: 'blocking' } -} - -export default Blog -``` - -When a request is made to a page that was pre-rendered at build time, it will initially show the cached page. - -- Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous. -- After the 10-second window, the next request will still show the cached (stale) page -- Next.js triggers a regeneration of the page in the background. -- Once the page has been successfully generated, Next.js will invalidate the cache and show the updated product page. If the background regeneration fails, the old page will stay unaltered. - -When a request is made to a path that hasn’t been generated, Next.js will server-render the page on the first request. Future requests will serve the static file from the cache. - -To learn how to persist the cache globally and handle rollbacks, learn more about [Incremental Static Regeneration](https://vercel.com/docs/next.js/incremental-static-regeneration). - -### Reading files: Use `process.cwd()` - -Files can be read directly from the filesystem in `getStaticProps`. - -In order to do so you have to get the full path to a file. - -Since Next.js compiles your code into a separate directory you can't use `__dirname` as the path it will return will be different from the pages directory. - -Instead you can use `process.cwd()` which gives you the directory where Next.js is being executed. - -```jsx -import { promises as fs } from 'fs' -import path from 'path' - -// posts will be populated at build time by getStaticProps() -function Blog({ posts }) { - return ( - <ul> - {posts.map((post) => ( - <li> - <h3>{post.filename}</h3> - <p>{post.content}</p> - </li> - ))} - </ul> - ) -} - -// This function gets called at build time on server-side. -// It won't be called on client-side, so you can even do -// direct database queries. See the "Technical details" section. -export async function getStaticProps() { - const postsDirectory = path.join(process.cwd(), 'posts') - const filenames = await fs.readdir(postsDirectory) - - const posts = filenames.map(async (filename) => { - const filePath = path.join(postsDirectory, filename) - const fileContents = await fs.readFile(filePath, 'utf8') - - // Generally you would parse/transform the contents - // For example you can transform markdown to HTML here - - return { - filename, - content: fileContents, - } - }) - // By returning { props: { posts } }, the Blog component - // will receive `posts` as a prop at build time - return { - props: { - posts: await Promise.all(posts), - }, - } -} - -export default Blog -``` - -### Technical details - -#### Only runs at build time - -Because `getStaticProps` runs at build time, it does **not** receive data that’s only available during request time, such as query parameters or HTTP headers as it generates static HTML. - -#### Write server-side code directly - -Note that `getStaticProps` runs only on the server-side. It will never be run on the client-side. It won’t even be included in the JS bundle for the browser. That means you can write code such as direct database queries without them being sent to browsers. You should not fetch an **API route** from `getStaticProps` — instead, you can write the server-side code directly in `getStaticProps`. - -You can use [this tool](https://next-code-elimination.vercel.app/) to verify what Next.js eliminates from the client-side bundle. - -#### Statically Generates both HTML and JSON - -When a page with `getStaticProps` is pre-rendered at build time, in addition to the page HTML file, Next.js generates a JSON file holding the result of running `getStaticProps`. - -This JSON file will be used in client-side routing through `next/link` ([documentation](/docs/api-reference/next/link.md)) or `next/router` ([documentation](/docs/api-reference/next/router.md)). When you navigate to a page that’s pre-rendered using `getStaticProps`, Next.js fetches this JSON file (pre-computed at build time) and uses it as the props for the page component. This means that client-side page transitions will **not** call `getStaticProps` as only the exported JSON is used. - -When using Incremental Static Generation `getStaticProps` will be executed out of band to generate the JSON needed for client-side navigation. You may see this in the form of multiple requests being made for the same page, however, this is intended and has no impact on end-user performance. - -#### Only allowed in a page - -`getStaticProps` can only be exported from a **page**. You can’t export it from non-page files. - -One of the reasons for this restriction is that React needs to have all the required data before the page is rendered. - -Also, you must use `export async function getStaticProps() {}` — it will **not** work if you add `getStaticProps` as a property of the page component. - -#### Runs on every request in development - -In development (`next dev`), `getStaticProps` will be called on every request. - -#### Preview Mode - -In some cases, you might want to temporarily bypass Static Generation and render the page at **request time** instead of build time. For example, you might be using a headless CMS and want to preview drafts before they're published. - -This use case is supported by Next.js by the feature called **Preview Mode**. Learn more on the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). - -## `getStaticPaths` (Static Generation) - -<details> - <summary><b>Version History</b></summary> - -| Version | Changes | -| -------- | ----------------------------------------------------------------------------------------------------------------- | -| `v9.5.0` | Stable [Incremental Static Regeneration](https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration) | -| `v9.3.0` | `getStaticPaths` introduced. | - -</details> - -If a page has dynamic routes ([documentation](/docs/routing/dynamic-routes.md)) and uses `getStaticProps` it needs to define a list of paths that have to be rendered to HTML at build time. - -If you export an `async` function called `getStaticPaths` from a page that uses dynamic routes, Next.js will statically pre-render all the paths specified by `getStaticPaths`. - -```jsx -export async function getStaticPaths() { - return { - paths: [ - { params: { ... } } // See the "paths" section below - ], - fallback: true, false, or 'blocking' // See the "fallback" section below - }; -} -``` - -#### The `paths` key (required) - -The `paths` key determines which paths will be pre-rendered. For example, suppose that you have a page that uses dynamic routes named `pages/posts/[id].js`. If you export `getStaticPaths` from this page and return the following for `paths`: - -```js -return { - paths: [ - { params: { id: '1' } }, - { params: { id: '2' } } - ], - fallback: ... -} -``` - -Then Next.js will statically generate `posts/1` and `posts/2` at build time using the page component in `pages/posts/[id].js`. - -Note that the value for each `params` must match the parameters used in the page name: - -- If the page name is `pages/posts/[postId]/[commentId]`, then `params` should contain `postId` and `commentId`. -- If the page name uses catch-all routes, for example `pages/[...slug]`, then `params` should contain `slug` which is an array. For example, if this array is `['foo', 'bar']`, then Next.js will statically generate the page at `/foo/bar`. -- If the page uses an optional catch-all route, supply `null`, `[]`, `undefined` or `false` to render the root-most route. For example, if you supply `slug: false` for `pages/[[...slug]]`, Next.js will statically generate the page `/`. - -#### The `fallback` key (required) - -The object returned by `getStaticPaths` must contain a boolean `fallback` key. - -#### `fallback: false` - -If `fallback` is `false`, then any paths not returned by `getStaticPaths` will result in a **404 page**. You can do this if you have a small number of paths to pre-render - so they are all statically generated during build time. It’s also useful when the new pages are not added often. If you add more items to the data source and need to render the new pages, you’d need to run the build again. - -Here’s an example which pre-renders one blog post per page called `pages/posts/[id].js`. The list of blog posts will be fetched from a CMS and returned by `getStaticPaths` . Then, for each page, it fetches the post data from a CMS using `getStaticProps`. This example is also in the [Pages documentation](/docs/basic-features/pages.md). - -```jsx -// pages/posts/[id].js - -function Post({ post }) { - // Render post... -} - -// This function gets called at build time -export async function getStaticPaths() { - // Call an external API endpoint to get posts - const res = await fetch('https://.../posts') - const posts = await res.json() - - // Get the paths we want to pre-render based on posts - const paths = posts.map((post) => ({ - params: { id: post.id }, - })) - - // We'll pre-render only these paths at build time. - // { fallback: false } means other routes should 404. - return { paths, fallback: false } -} - -// This also gets called at build time -export async function getStaticProps({ params }) { - // params contains the post `id`. - // If the route is like /posts/1, then params.id is 1 - const res = await fetch(`https://.../posts/${params.id}`) - const post = await res.json() - - // Pass post data to the page via props - return { props: { post } } -} - -export default Post -``` - -#### `fallback: true` - -<details> - <summary><b>Examples</b></summary> - <ul> - <li><a href="https://static-tweet.vercel.app">Static generation of a large number of pages</a></li> - </ul> -</details> - -If `fallback` is `true`, then the behavior of `getStaticProps` changes: - -- The paths returned from `getStaticPaths` will be rendered to HTML at build time by `getStaticProps`. -- The paths that have not been generated at build time will **not** result in a 404 page. Instead, Next.js will serve a “fallback” version of the page on the first request to such a path (see [“Fallback pages”](#fallback-pages) below for details). -- In the background, Next.js will statically generate the requested path HTML and JSON. This includes running `getStaticProps`. -- When that’s done, the browser receives the JSON for the generated path. This will be used to automatically render the page with the required props. From the user’s perspective, the page will be swapped from the fallback page to the full page. -- At the same time, Next.js adds this path to the list of pre-rendered pages. Subsequent requests to the same path will serve the generated page, like other pages pre-rendered at build time. - -> `fallback: true` is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). - -#### Fallback pages - -In the “fallback” version of a page: - -- The page’s props will be empty. -- Using the [router](/docs/api-reference/next/router.md), you can detect if the fallback is being rendered, `router.isFallback` will be `true`. - -Here’s an example that uses `isFallback`: - -```jsx -// pages/posts/[id].js -import { useRouter } from 'next/router' - -function Post({ post }) { - const router = useRouter() - - // If the page is not yet generated, this will be displayed - // initially until getStaticProps() finishes running - if (router.isFallback) { - return <div>Loading...</div> - } - - // Render post... -} - -// This function gets called at build time -export async function getStaticPaths() { - return { - // Only `/posts/1` and `/posts/2` are generated at build time - paths: [{ params: { id: '1' } }, { params: { id: '2' } }], - // Enable statically generating additional pages - // For example: `/posts/3` - fallback: true, - } -} - -// This also gets called at build time -export async function getStaticProps({ params }) { - // params contains the post `id`. - // If the route is like /posts/1, then params.id is 1 - const res = await fetch(`https://.../posts/${params.id}`) - const post = await res.json() - - // Pass post data to the page via props - return { - props: { post }, - // Re-generate the post at most once per second - // if a request comes in - revalidate: 1, - } -} - -export default Post -``` - -#### When is `fallback: true` useful? - -`fallback: true` is useful if your app has a very large number of static pages that depend on data (think: a very large e-commerce site). You want to pre-render all product pages, but then your builds would take forever. - -Instead, you may statically generate a small subset of pages and use `fallback: true` for the rest. When someone requests a page that’s not generated yet, the user will see the page with a loading indicator. Shortly after, `getStaticProps` finishes and the page will be rendered with the requested data. From now on, everyone who requests the same page will get the statically pre-rendered page. - -This ensures that users always have a fast experience while preserving fast builds and the benefits of Static Generation. - -`fallback: true` will not _update_ generated pages, for that take a look at [Incremental Static Regeneration](#incremental-static-regeneration). - -#### `fallback: 'blocking'` - -If `fallback` is `'blocking'`, new paths not returned by `getStaticPaths` will wait for the HTML to be generated, identical to SSR (hence why _blocking_), and then be cached for future requests so it only happens once per path. - -`getStaticProps` will behave as follows: - -- The paths returned from `getStaticPaths` will be rendered to HTML at build time by `getStaticProps`. -- The paths that have not been generated at build time will **not** result in a 404 page. Instead, Next.js will SSR on the first request and return the generated HTML. -- When that’s done, the browser receives the HTML for the generated path. From the user’s perspective, it will transition from "the browser is requesting the page" to "the full page is loaded". There is no flash of loading/fallback state. -- At the same time, Next.js adds this path to the list of pre-rendered pages. Subsequent requests to the same path will serve the generated page, like other pages pre-rendered at build time. - -`fallback: 'blocking'` will not _update_ generated pages by default. To update generated pages, use [Incremental Static Regeneration](#incremental-static-regeneration) in conjunction with `fallback: 'blocking'`. - -> `fallback: 'blocking'` is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). - -### When should I use `getStaticPaths`? - -You should use `getStaticPaths` if you’re statically pre-rendering pages that use dynamic routes. - -### TypeScript: Use `GetStaticPaths` - -For TypeScript, you can use the `GetStaticPaths` type from `next`: - -```ts -import { GetStaticPaths } from 'next' - -export const getStaticPaths: GetStaticPaths = async () => { - // ... -} -``` - -### Technical details - -#### Use together with `getStaticProps` - -When you use `getStaticProps` on a page with dynamic route parameters, you must use `getStaticPaths`. - -You cannot use `getStaticPaths` with `getServerSideProps`. - -#### Only runs at build time on server-side - -`getStaticPaths` only runs at build time on server-side. - -#### Only allowed in a page - -`getStaticPaths` can only be exported from a **page**. You can’t export it from non-page files. - -Also, you must use `export async function getStaticPaths() {}` — it will **not** work if you add `getStaticPaths` as a property of the page component. - -#### Runs on every request in development - -In development (`next dev`), `getStaticPaths` will be called on every request. - -## `getServerSideProps` (Server-side Rendering) - -<details> - <summary><b>Version History</b></summary> - -| Version | Changes | -| --------- | ------------------------------------------------------------------- | -| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | -| `v9.3.0` | `getServerSideProps` introduced. | - -</details> - -If you export an `async` function called `getServerSideProps` from a page, Next.js will pre-render this page on each request using the data returned by `getServerSideProps`. - -```js -export async function getServerSideProps(context) { - return { - props: {}, // will be passed to the page component as props - } -} -``` - -The `context` parameter is an object containing the following keys: - -- `params`: If this page uses a dynamic route, `params` contains the route parameters. If the page name is `[id].js` , then `params` will look like `{ id: ... }`. To learn more, take a look at the [Dynamic Routing documentation](/docs/routing/dynamic-routes.md). -- `req`: [The HTTP IncomingMessage object](https://nodejs.org/api/http.html#http_class_http_incomingmessage). -- `res`: [The HTTP response object](https://nodejs.org/api/http.html#http_class_http_serverresponse). -- `query`: An object representing the query string. -- `preview`: `preview` is `true` if the page is in the preview mode and `false` otherwise. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). -- `previewData`: The preview data set by `setPreviewData`. See the [Preview Mode documentation](/docs/advanced-features/preview-mode.md). -- `resolvedUrl`: A normalized version of the request URL that strips the `_next/data` prefix for client transitions and includes original query values. -- `locale` contains the active locale (if enabled). -- `locales` contains all supported locales (if enabled). -- `defaultLocale` contains the configured default locale (if enabled). - -`getServerSideProps` should return an object with: - -- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) or a Promise that resolves to a serializable object. -- `notFound` - An **optional** boolean value to allow the page to return a 404 status and page. Below is an example of how it works: - - ```js - export async function getServerSideProps(context) { - const res = await fetch(`https://...`) - const data = await res.json() - - if (!data) { - return { - notFound: true, - } - } - - return { - props: {}, // will be passed to the page component as props - } - } - ``` - -- `redirect` - An **optional** redirect value to allow redirecting to internal and external resources. It should match the shape of `{ destination: string, permanent: boolean }`. In some rare cases, you might need to assign a custom status code for older HTTP Clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both. Below is an example of how it works: - - ```js - export async function getServerSideProps(context) { - const res = await fetch(`https://.../data`) - const data = await res.json() - - if (!data) { - return { - redirect: { - destination: '/', - permanent: false, - }, - } - } - - return { - props: {}, // will be passed to the page component as props - } - } - ``` - -> **Note**: You can import modules in top-level scope for use in `getServerSideProps`. -> Imports used in `getServerSideProps` will not be bundled for the client-side. -> -> This means you can write **server-side code directly in `getServerSideProps`**. -> This includes reading from the filesystem or a database. - -> **Note**: You should not use [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to -> call an API route in `getServerSideProps`. -> Instead, directly import the logic used inside your API route. -> You may need to slightly refactor your code for this approach. -> -> Fetching from an external API is fine! - -### Example - -Here’s an example which uses `getServerSideProps` to fetch data at request time and pre-renders it. This example is also in the [Pages documentation](/docs/basic-features/pages.md). - -```jsx -function Page({ data }) { - // Render data... -} - -// This gets called on every request -export async function getServerSideProps() { - // Fetch data from external API - const res = await fetch(`https://.../data`) - const data = await res.json() - - // Pass data to the page via props - return { props: { data } } -} - -export default Page -``` - -### When should I use `getServerSideProps`? - -You should use `getServerSideProps` only if you need to pre-render a page whose data must be fetched at request time. Time to first byte (TTFB) will be slower than `getStaticProps` because the server must compute the result on every request, and the result cannot be cached by a CDN without extra configuration. - -If you don’t need to pre-render the data, then you should consider fetching data on the client side. [Click here to learn more](#fetching-data-on-the-client-side). - -### TypeScript: Use `GetServerSideProps` - -For TypeScript, you can use the `GetServerSideProps` type from `next`: - -```ts -import { GetServerSideProps } from 'next' - -export const getServerSideProps: GetServerSideProps = async (context) => { - // ... -} -``` - -If you want to get inferred typings for your props, you can use `InferGetServerSidePropsType<typeof getServerSideProps>`, like this: - -```tsx -import { InferGetServerSidePropsType } from 'next' - -type Data = { ... } - -export const getServerSideProps = async () => { - const res = await fetch('https://.../data') - const data: Data = await res.json() - - return { - props: { - data, - }, - } -} - -function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) { - // will resolve posts to type Data -} - -export default Page -``` - -### Technical details - -#### Only runs on server-side - -`getServerSideProps` only runs on server-side and never runs on the browser. If a page uses `getServerSideProps`, then: - -- When you request this page directly, `getServerSideProps` runs at the request time, and this page will be pre-rendered with the returned props. -- When you request this page on client-side page transitions through `next/link` ([documentation](/docs/api-reference/next/link.md)) or `next/router` ([documentation](/docs/api-reference/next/router.md)), Next.js sends an API request to the server, which runs `getServerSideProps`. It’ll return JSON that contains the result of running `getServerSideProps`, and the JSON will be used to render the page. All this work will be handled automatically by Next.js, so you don’t need to do anything extra as long as you have `getServerSideProps` defined. - -You can use [this tool](https://next-code-elimination.vercel.app/) to verify what Next.js eliminates from the client-side bundle. - -#### Only allowed in a page - -`getServerSideProps` can only be exported from a **page**. You can’t export it from non-page files. - -Also, you must use `export async function getServerSideProps() {}` — it will **not** work if you add `getServerSideProps` as a property of the page component. - -## Fetching data on the client side - -If your page contains frequently updating data, and you don’t need to pre-render the data, you can fetch the data on the client side. An example of this is user-specific data. Here’s how it works: - -- First, immediately show the page without data. Parts of the page can be pre-rendered using Static Generation. You can show loading states for missing data. -- Then, fetch the data on the client side and display it when ready. - -This approach works well for user dashboard pages, for example. Because a dashboard is a private, user-specific page, SEO is not relevant and the page doesn’t need to be pre-rendered. The data is frequently updated, which requires request-time data fetching. - -### SWR - -The team behind Next.js has created a React hook for data fetching called [**SWR**](https://swr.vercel.app/). We highly recommend it if you’re fetching data on the client side. It handles caching, revalidation, focus tracking, refetching on interval, and more. And you can use it like so: - -```jsx -import useSWR from 'swr' - -const fetcher = (url) => fetch(url).then((res) => res.json()) - -function Profile() { - const { data, error } = useSWR('/api/user', fetcher) - - if (error) return <div>failed to load</div> - if (!data) return <div>loading...</div> - return <div>hello {data.name}!</div> -} -``` - -[Check out the SWR documentation to learn more](https://swr.vercel.app/). - -## Learn more - -We recommend you to read the following sections next: - -<div class="card"> - <a href="/docs/advanced-features/preview-mode.md"> - <b>Preview Mode:</b> - <small>Learn more about the preview mode in Next.js.</small> - </a> -</div> - -<div class="card"> - <a href="/docs/routing/introduction.md"> - <b>Routing:</b> - <small>Learn more about routing in Next.js.</small> - </a> -</div> - -<div class="card"> - <a href="/docs/basic-features/typescript.md#pages"> - <b>TypeScript:</b> - <small>Add TypeScript to your pages.</small> - </a> -</div> diff --git a/docs/basic-features/data-fetching/client-side.md b/docs/basic-features/data-fetching/client-side.md new file mode 100644 index 000000000000..97cad41a1a8a --- /dev/null +++ b/docs/basic-features/data-fetching/client-side.md @@ -0,0 +1,70 @@ +--- +description: 'Learn about client-side data fetching, and how to use SWR, a data fetching React hook library that handles caching, revalidation, focus tracking, refetching on interval and more.' +--- + +# Client-side data fetching + +Client-side data fetching is useful when your page doesn't require SEO indexing, when you don't need to pre-render your data, or when the content of your pages needs to update frequently. Unlike the server-side rendering APIs, you can use client-side data fetching at the component level. + +If done at the page level, the data is fetched at runtime, and the content of the page is updated as the data changes. When used at the component level, the data is fetched at the time of the component mount, and the content of the component is updated as the data changes. + +It's important to note that using client-side data fetching can affect the performance of your application and the load speed of your pages. This is because the data fetching is done at the time of the component or pages mount, and the data is not cached. + +## Client-side data fetching with useEffect + +The following example shows how you can fetch data on the client side using the useEffect hook. + +```jsx +function Profile() { + const [data, setData] = useState(null) + const [isLoading, setLoading] = useState(false) + + useEffect(() => { + setLoading(true) + fetch('api/profile-data') + .then((res) => res.json()) + .then((data) => { + setData(data) + setLoading(false) + }) + }, []) + + if (isLoading) return <p>Loading...</p> + if (!data) return <p>No profile data</p> + + return ( + <div> + <h1>{data.name}</h1> + <p>{data.bio}</p> + </div> + ) +} +``` + +## Client-side data fetching with SWR + +The team behind Next.js has created a React hook library for data fetching called [**SWR**](https://swr.vercel.app/). It is **highly recommended** if you are fetching data on the client-side. It handles caching, revalidation, focus tracking, refetching on intervals, and more. + +Using the same example as above, we can now use SWR to fetch the profile data. SWR will automatically cache the data for us and will revalidate the data if it becomes stale. + +For more information on using SWR, check out the [SWR docs](https://swr.vercel.app/docs/getting-started). + +```jsx +import useSWR from 'swr' + +const fetcher = (...args) => fetch(...args).then((res) => res.json()) + +function Profile() { + const { data, error } = useSWR('/api/profile-data', fetcher) + + if (error) return <div>Failed to load</div> + if (!data) return <div>Loading...</div> + + return ( + <div> + <h1>{data.name}</h1> + <p>{data.bio}</p> + </div> + ) +} +``` diff --git a/docs/basic-features/data-fetching/get-server-side-props.md b/docs/basic-features/data-fetching/get-server-side-props.md new file mode 100644 index 000000000000..53952a44942e --- /dev/null +++ b/docs/basic-features/data-fetching/get-server-side-props.md @@ -0,0 +1,82 @@ +--- +description: Fetch data on each request with `getServerSideProps`. +--- + +# getServerSideProps + +If you export a function called `getServerSideProps` (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by `getServerSideProps`. + +```js +export async function getServerSideProps(context) { + return { + props: {}, // will be passed to the page component as props + } +} +``` + +## When does getServerSideProps run + +`getServerSideProps` only runs on server-side and never runs on the browser. If a page uses `getServerSideProps`, then: + +- When you request this page directly, `getServerSideProps` runs at request time, and this page will be pre-rendered with the returned props +- When you request this page on client-side page transitions through [`next/link`](/docs/api-reference/next/link.md) or [`next/router`](/docs/api-reference/next/router.md), Next.js sends an API request to the server, which runs `getServerSideProps` + +It then returns `JSON` that contains the result of running `getServerSideProps`, that `JSON` will be used to render the page. All this work will be handled automatically by Next.js, so you don’t need to do anything extra as long as you have `getServerSideProps` defined. + +You can use the [next-code-elimination tool](https://next-code-elimination.vercel.app/) to verify what Next.js eliminates from the client-side bundle. + +`getServerSideProps` can only be exported from a **page**. You can’t export it from non-page files. + +Note that you must export `getServerSideProps` as a standalone function — it will **not** work if you add `getServerSideProps` as a property of the page component. + +The [`getServerSideProps` API reference](/docs/api-reference/data-fetching/get-server-side-props.md) covers all parameters and props that can be used with `getServerSideProps`. + +## When should I use getServerSideProps + +You should use `getServerSideProps` only if you need to pre-render a page whose data must be fetched at request time. [Time to First Byte (TTFB)](https://web.dev/ttfb/) will be higher than [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) because the server must compute the result on every request, and the result can only be cached by a CDN using `cache-control` headers (which could require extra configuration). + +If you do not need to pre-render the data, then you should consider fetching data on the [client side](#fetching-data-on-the-client-side). + +### getServerSideProps or API Routes + +It can be tempting to reach for an [API Route](/docs/api-routes/introduction.md) when you want to fetch data from the server, then call that API route from `getServerSideProps`. This is an unnecessary and inefficient approach, as it will cause an extra request to be made due to both `getServerSideProps` and API Routes running on the server. + +Take the following example. An API route is used to fetch some data from a CMS. That API route is then called directly from `getServerSideProps`. This produces an additional call, reducing performance. Instead, directly import the logic used inside your API Route into `getServerSideProps`. This could mean calling a CMS, database, or other API directly from inside `getServerSideProps`. + +## Fetching data on the client side + +If your page contains frequently updating data, and you don’t need to pre-render the data, you can fetch the data on the [client side](/docs/basic-features/data-fetching/client-side.md). An example of this is user-specific data: + +- First, immediately show the page without data. Parts of the page can be pre-rendered using Static Generation. You can show loading states for missing data. +- Then, fetch the data on the client side and display it when ready. + +This approach works well for user dashboard pages, for example. Because a dashboard is a private, user-specific page, SEO is not relevant and the page doesn’t need to be pre-rendered. The data is frequently updated, which requires request-time data fetching. + +## Using getServerSideProps to fetch data at request time + +The following example shows how to fetch data at request time and pre-render the result. + +```jsx +function Page({ data }) { + // Render data... +} + +// This gets called on every request +export async function getServerSideProps() { + // Fetch data from external API + const res = await fetch(`https://.../data`) + const data = await res.json() + + // Pass data to the page via props + return { props: { data } } +} + +export default Page +``` + +<div class="card"> + <a href="/docs/api-reference/data-fetching/get-server-side-props.md"> + <b>getServerSideProps API Reference</b> + <small>Read the API Reference for getServerSideProps</small> + </a> +</div> diff --git a/docs/basic-features/data-fetching/get-static-paths.md b/docs/basic-features/data-fetching/get-static-paths.md new file mode 100644 index 000000000000..cb9b5b68b438 --- /dev/null +++ b/docs/basic-features/data-fetching/get-static-paths.md @@ -0,0 +1,55 @@ +--- +description: Fetch data and generate static pages with `getStaticProps`. Learn more about this API for data fetching in Next.js. +--- + +# getStaticPaths + +If a page has [Dynamic Routes](/docs/routing/dynamic-routes.md) and uses `getStaticProps`, it needs to define a list of paths to be statically generated. + +When you export a function called `getStaticPaths` (Static Site Generation) from a page that uses dynamic routes, Next.js will statically pre-render all the paths specified by `getStaticPaths`. + +```jsx +export async function getStaticPaths() { + return { + paths: [ + { params: { ... } } + ], + fallback: true // false or 'blocking' + }; +} +``` + +Note that`getStaticProps` **must** be used with `getStaticPaths`, and that you **cannot** use it with [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md). + +The [`getStaticPaths` API reference](/docs/api-reference/data-fetching/get-static-paths.md) covers all parameters and props that can be used with `getStaticPaths`. + +## When should I use getStaticPaths? + +You should use `getStaticPaths` if you’re statically pre-rendering pages that use dynamic routes and: + +- The data comes from a headless CMS +- The data comes from a database +- The data comes from the filesystem +- The data can be publicly cached (not user-specific) +- The page must be pre-rendered (for SEO) and be very fast — `getStaticProps` generates `HTML` and `JSON` files, both of which can be cached by a CDN for performance + +## When does getStaticPaths run + +`getStaticPaths` only runs at build time on server-side. If you're using [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md), `getStaticPaths` can also be run on-demand _in the background_, but still only on the server-side. + +## Where can I use getStaticPaths + +`getStaticPaths` can only be exported from a **page**. You **cannot** export it from non-page files. + +Note that you must use export `getStaticPaths` as a standalone function — it will **not** work if you add `getStaticPaths` as a property of the page component. + +## Runs on every request in development + +In development (`next dev`), `getStaticPaths` will be called on every request. + +<div class="card"> + <a href="/docs/api-reference/data-fetching/get-static-paths.md"> + <b>getStaticPaths API Reference</b> + <small>Read the API Reference for getStaticPaths</small> + </a> +</div> diff --git a/docs/basic-features/data-fetching/get-static-props.md b/docs/basic-features/data-fetching/get-static-props.md new file mode 100644 index 000000000000..63e31187777a --- /dev/null +++ b/docs/basic-features/data-fetching/get-static-props.md @@ -0,0 +1,112 @@ +--- +description: Fetch data and generate static pages with `getStaticProps`. Learn more about this API for data fetching in Next.js. +--- + +# getStaticProps + +If you export a function called `getStaticProps` (Static Site Generation) from a page, Next.js will pre-render this page at build time using the props returned by `getStaticProps`. + +```jsx +export async function getStaticProps(context) { + return { + props: {}, // will be passed to the page component as props + } +} +``` + +## When should I use getStaticProps? + +You should use `getStaticProps` if: + +- The data required to render the page is available at build time ahead of a user’s request +- The data comes from a headless CMS +- The data can be publicly cached (not user-specific) +- The page must be pre-rendered (for SEO) and be very fast — `getStaticProps` generates `HTML` and `JSON` files, both of which can be cached by a CDN for performance + +When combined with [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md), `getStaticProps` will run in the background while the stale page is being revalidated, and the fresh page served to the browser. + +Because `getStaticProps` runs at build time, it does **not** have access to the incoming request (such as query parameters or `HTTP` headers) as it generates static `HTML`. If you need access to the request for your page, consider using [Middleware](/docs/middleware.md) in addition to `getStaticProps`. + +## Using getStaticProps to fetch data from a CMS + +The following example shows how you can fetch a list of blog posts from a CMS. + +```jsx +// posts will be populated at build time by getStaticProps() +function Blog({ posts }) { + return ( + <ul> + {posts.map((post) => ( + <li>{post.title}</li> + ))} + </ul> + ) +} + +// This function gets called at build time on server-side. +// It won't be called on client-side, so you can even do +// direct database queries. +export async function getStaticProps() { + // Call an external API endpoint to get posts. + // You can use any data fetching library + const res = await fetch('https://.../posts') + const posts = await res.json() + + // By returning { props: { posts } }, the Blog component + // will receive `posts` as a prop at build time + return { + props: { + posts, + }, + } +} + +export default Blog +``` + +The [`getStaticProps` API reference](/docs/api-reference/data-fetching/get-static-props.md) covers all parameters and props that can be used with `getStaticProps`. + +## Write server-side code directly + +As `getStaticProps` runs only on the server-side, it will never run on the client-side. It won’t even be included in the JS bundle for the browser, so you can write direct database queries without them being sent to browsers. + +This means that instead of fetching an **API route** from `getStaticProps` (that itself fetches data from an external source), you can write the server-side code directly in `getStaticProps`. + +Take the following example. An API route is used to fetch some data from a CMS. That API route is then called directly from `getStaticProps`. This produces an additional call, reducing performance. Instead, the logic for fetching the data from the CMS can be moved to `getStaticProps`. + +Alternatively, if you are **not** using API routes to fetch data, then the [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API _can_ be used directly in `getStaticProps` to fetch data. + +To verify what Next.js eliminates from the client-side bundle, you can use the [next-code-elimination tool](https://next-code-elimination.vercel.app/). + +## Statically generates both HTML and JSON + +When a page with `getStaticProps` is pre-rendered at build time, in addition to the page HTML file, Next.js generates a JSON file holding the result of running `getStaticProps`. + +This JSON file will be used in client-side routing through [`next/link`](/docs/api-reference/next/link.md) or [`next/router`](/docs/api-reference/next/router.md). When you navigate to a page that’s pre-rendered using `getStaticProps`, Next.js fetches this JSON file (pre-computed at build time) and uses it as the props for the page component. This means that client-side page transitions will **not** call `getStaticProps` as only the exported JSON is used. + +When using Incremental Static Generation, `getStaticProps` will be executed in the background to generate the JSON needed for client-side navigation. You may see this in the form of multiple requests being made for the same page, however, this is intended and has no impact on end-user performance. + +## Where can I use getStaticProps + +`getStaticProps` can only be exported from a **page**. You **cannot** export it from non-page files. + +One of the reasons for this restriction is that React needs to have all the required data before the page is rendered. + +Also, you must use export `getStaticProps` as a standalone function — it will **not** work if you add `getStaticProps` as a property of the page component. + +## Runs on every request in development + +In development (`next dev`), `getStaticProps` will be called on every request. + +## Preview Mode + +In some cases, you might want to temporarily bypass Static Generation and render the page at **request time** instead of build time. For example, you might be using a headless CMS and want to preview drafts before they're published. + +This use case is supported in Next.js by the [**Preview Mode**](/docs/advanced-features/preview-mode.md) feature. + +<div class="card"> + <a href="/docs/api-reference/data-fetching/get-static-props.md"> + <b>getStaticProps API Reference</b> + <small>Read the API Reference for getStaticProps</small> + </a> +</div> diff --git a/docs/basic-features/data-fetching/incremental-static-regeneration.md b/docs/basic-features/data-fetching/incremental-static-regeneration.md new file mode 100644 index 000000000000..51b7a542e6ce --- /dev/null +++ b/docs/basic-features/data-fetching/incremental-static-regeneration.md @@ -0,0 +1,88 @@ +--- +description: 'Learn how to create or update static pages at runtime with Incremental Static Regeneration.' +--- + +# Incremental Static Regeneration + +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://nextjs.org/commerce">Next.js Commerce</a></li> + <li><a href="https://reactions-demo.vercel.app/">GitHub Reactions Demo</a></li> + <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> + </ul> +</details> + +<details> + <summary><b>Version History</b></summary> + +| Version | Changes | +| -------- | ---------------- | +| `v9.5.0` | Base Path added. | + +</details> + +Next.js allows you to create or update static pages _after_ you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, **without needing to rebuild the entire site**. With ISR, you can retain the benefits of static while scaling to millions of pages. + +To use ISR add the `revalidate` prop to `getStaticProps`: + +```jsx +function Blog({ posts }) { + return ( + <ul> + {posts.map((post) => ( + <li key={post.id}>{post.title}</li> + ))} + </ul> + ) +} + +// This function gets called at build time on server-side. +// It may be called again, on a serverless function, if +// revalidation is enabled and a new request comes in +export async function getStaticProps() { + const res = await fetch('https://.../posts') + const posts = await res.json() + + return { + props: { + posts, + }, + // Next.js will attempt to re-generate the page: + // - When a request comes in + // - At most once every 10 seconds + revalidate: 10, // In seconds + } +} + +// This function gets called at build time on server-side. +// It may be called again, on a serverless function, if +// the path has not been generated. +export async function getStaticPaths() { + const res = await fetch('https://.../posts') + const posts = await res.json() + + // Get the paths we want to pre-render based on posts + const paths = posts.map((post) => ({ + params: { id: post.id }, + })) + + // We'll pre-render only these paths at build time. + // { fallback: blocking } will server-render pages + // on-demand if the path doesn't exist. + return { paths, fallback: 'blocking' } +} + +export default Blog +``` + +When a request is made to a page that was pre-rendered at build time, it will initially show the cached page. + +- Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous. +- After the 10-second window, the next request will still show the cached (stale) page +- Next.js triggers a regeneration of the page in the background. +- Once the page has been successfully generated, Next.js will invalidate the cache and show the updated page. If the background regeneration fails, the old page would still be unaltered. + +When a request is made to a path that hasn’t been generated, Next.js will server-render the page on the first request. Future requests will serve the static file from the cache. + +[Incremental Static Regeneration](https://vercel.com/docs/concepts/next.js/incremental-static-regeneration) covers how to persist the cache globally and handle rollbacks. diff --git a/docs/basic-features/data-fetching/overview.md b/docs/basic-features/data-fetching/overview.md new file mode 100644 index 000000000000..290e189dbe45 --- /dev/null +++ b/docs/basic-features/data-fetching/overview.md @@ -0,0 +1,87 @@ +--- +description: 'Next.js allows you to fetch data in multiple ways, with pre-rendering, server-side rendering or static-site generation, and incremental static regeneration. Learn how to manage your application data in Next.js.' +--- + +# Data Fetching Overview + +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-wordpress">WordPress Example</a> (<a href="https://next-blog-wordpress.vercel.app">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter">Blog Starter using markdown files</a> (<a href="https://next-blog-starter.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-datocms">DatoCMS Example</a> (<a href="https://next-blog-datocms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape">TakeShape Example</a> (<a href="https://next-blog-takeshape.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity">Sanity Example</a> (<a href="https://next-blog-sanity.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prismic">Prismic Example</a> (<a href="https://next-blog-prismic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful Example</a> (<a href="https://next-blog-contentful.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-strapi">Strapi Example</a> (<a href="https://next-blog-strapi.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-prepr">Prepr Example</a> (<a href="https://next-blog-prepr.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-agilitycms">Agility CMS Example</a> (<a href="https://next-blog-agilitycms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic">Cosmic Example</a> (<a href="https://next-blog-cosmic.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-buttercms">ButterCMS Example</a> (<a href="https://next-blog-buttercms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app/">Demo</a>)</li> + <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> + </ul> +</details> + +Data fetching in Next.js allows you to render your content in different ways, depending on your application's use case. These include pre-rendering with **Server-side Rendering** or **Static Generation**, and updating or creating content at runtime with **Incremental Static Regeneration**. + +<div class="card"> + <a href="/docs/basic-features/data-fetching/get-server-side-props.md"> + <b>SSR: Server-side rendering</b> + <small>Learn more about server-side rendering in Next.js with getServerSideProps.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/data-fetching/get-static-props.md"> + <b>SSG: Static-site generation</b> + <small>Learn more about static site generation in Next.js with getStaticProps.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/data-fetching/client-side.md"> + <b>CSR: Client-side rendering</b> + <small>Learn more about client side rendering in Next.js with SWR.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/data-fetching/get-static-paths.md"> + <b>Dynamic routing</b> + <small>Learn more about dynamic routing in Next.js with getStaticPaths.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/data-fetching/incremental-static-regeneration.md"> + <b>ISR: Incremental Static Regeneration</b> + <small>Learn more about Incremental Static Regeneration in Next.js.</small> + </a> +</div> + +## Learn more + +<div class="card"> + <a href="/docs/advanced-features/preview-mode.md"> + <b>Preview Mode:</b> + <small>Learn more about the preview mode in Next.js.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/routing/introduction.md"> + <b>Routing:</b> + <small>Learn more about routing in Next.js.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/basic-features/typescript.md#pages"> + <b>TypeScript:</b> + <small>Add TypeScript to your pages.</small> + </a> +</div> diff --git a/docs/basic-features/environment-variables.md b/docs/basic-features/environment-variables.md index 93f65cfdc3a3..3b589e4b75fa 100644 --- a/docs/basic-features/environment-variables.md +++ b/docs/basic-features/environment-variables.md @@ -30,9 +30,9 @@ DB_USER=myuser DB_PASS=mypassword ``` -This loads `process.env.DB_HOST`, `process.env.DB_USER`, and `process.env.DB_PASS` into the Node.js environment automatically allowing you to use them in [Next.js data fetching methods](/docs/basic-features/data-fetching.md) and [API routes](/docs/api-routes/introduction.md). +This loads `process.env.DB_HOST`, `process.env.DB_USER`, and `process.env.DB_PASS` into the Node.js environment automatically allowing you to use them in [Next.js data fetching methods](/docs/basic-features/data-fetching/overview.md) and [API routes](/docs/api-routes/introduction.md). -For example, using [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation): +For example, using [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md): ```js // pages/index.js @@ -49,7 +49,7 @@ export async function getStaticProps() { > **Note**: In order to keep server-only secrets safe, Next.js replaces `process.env.*` with the correct values > at build time. This means that `process.env` is not a standard JavaScript object, so you’re not able to > use [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment). -> Environment variables must be referenced as e.g. `process.env.NEXT_PUBLIC_PUBLISHABLE_KEY`, _not_ `const { NEXT_PUBLIC_PUBLISHABLE_KEY } = process.env`. +> Environment variables must be referenced as e.g. `process.env.PUBLISHABLE_KEY`, _not_ `const { PUBLISHABLE_KEY } = process.env`. > **Note**: Next.js will automatically expand variables (`$VAR`) inside of your `.env*` files. > This allows you to reference other secrets, like so: @@ -76,6 +76,8 @@ export async function getStaticProps() { > CORRECT=pre\$A > ``` +> **Note**: If you are using a `/src` folder, please note that Next.js will load the .env files **only** from the parent folder and **not** from the `/src` folder. + ## Exposing Environment Variables to the Browser By default environment variables are only available in the Node.js environment, meaning they won't be exposed to the browser. @@ -110,7 +112,7 @@ Next.js allows you to set defaults in `.env` (all environments), `.env.developme `.env.local` always overrides the defaults set. -> **Note**: `.env`, `.env.development`, and `.env.production` files should be included in your repository as they define defaults. **`.env.*.local` should be added to `.gitignore`**, as those files are intended to be ignored. `.env.local` is where secrets can be stored. +> **Note**: `.env`, `.env.development`, and `.env.production` files should be included in your repository as they define defaults. **`.env*.local` should be added to `.gitignore`**, as those files are intended to be ignored. `.env.local` is where secrets can be stored. ## Environment Variables on Vercel @@ -128,11 +130,11 @@ When using the Vercel CLI to deploy make sure you add a [`.vercelignore`](https: ## Test Environment Variables -Apart from `development` and `production` environments, there is a 3rd option available: `test`. In the same way you can set defaults for development or production environments, you can do the same with `.env.test` file for testing environment (though this one is not so common as the previous two). +Apart from `development` and `production` environments, there is a 3rd option available: `test`. In the same way you can set defaults for development or production environments, you can do the same with a `.env.test` file for the `testing` environment (though this one is not as common as the previous two). This one is useful when running tests with tools like `jest` or `cypress` where you need to set specific environment vars only for testing purposes. Test default values will be loaded if `NODE_ENV` is set to `test`, though you usually don't need to do this manually as testing tools will address it for you. -There is a small difference between `test` environment, and both `development` and `production` that you need to bear in mind: `.env.local` won't be loaded, as you expect tests to produce the same results for everyone. This way every test execution will use same env defaults across different executions by ignoring your `.env.local` (which is intended to override the default set). +There is a small difference between `test` environment, and both `development` and `production` that you need to bear in mind: `.env.local` won't be loaded, as you expect tests to produce the same results for everyone. This way every test execution will use the same env defaults across different executions by ignoring your `.env.local` (which is intended to override the default set). > **Note**: similar to Default Environment Variables, `.env.test` file should be included in your repository, but `.env.test.local` shouldn't, as `.env*.local` are intended to be ignored through `.gitignore`. @@ -147,3 +149,29 @@ export default async () => { loadEnvConfig(projectDir) } ``` + +## Environment Variable Load Order + +Depending on the environment (as set by `NODE_ENV`), variables are loaded from the following sources in order from top-to-bottom. In all environments existing env is not overridden by following sources. + +`NODE_ENV=production` + +- `.env.production.local` +- `.env.local` +- `.env.production` +- `.env` + +`NODE_ENV=development` + +- `.env.development.local` +- `.env.local` +- `.env.development` +- `.env` + +`NODE_ENV=test` + +- `.env.test.local` +- `.env.test` +- `.env` + +_(note: `.env.local` is not loaded when `NODE_ENV=test`)_ diff --git a/docs/basic-features/eslint.md b/docs/basic-features/eslint.md index 6d3028a6a5f1..fd342db4c80f 100644 --- a/docs/basic-features/eslint.md +++ b/docs/basic-features/eslint.md @@ -90,6 +90,7 @@ Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/ | ✔️ | [next/no-head-import-in-document](https://nextjs.org/docs/messages/no-head-import-in-document) | Disallow importing next/head in pages/document.js | | ✔️ | [next/no-html-link-for-pages](https://nextjs.org/docs/messages/no-html-link-for-pages) | Prohibit HTML anchor links to pages without a Link component | | ✔️ | [next/no-img-element](https://nextjs.org/docs/messages/no-img-element) | Prohibit usage of HTML <img> element | +| ✔️ | [next/no-head-element](https://nextjs.org/docs/messages/no-head-element) | Prohibit usage of HTML <head> element | | ✔️ | [next/no-page-custom-font](https://nextjs.org/docs/messages/no-page-custom-font) | Prevent page-only custom fonts | | ✔️ | [next/no-sync-scripts](https://nextjs.org/docs/messages/no-sync-scripts) | Forbid synchronous scripts | | ✔️ | [next/no-title-in-document-head](https://nextjs.org/docs/messages/no-title-in-document-head) | Disallow using <title> with Head from next/document | @@ -113,15 +114,15 @@ If you're using `eslint-plugin-next` in a project where Next.js isn't installed "extends": "next", "settings": { "next": { - "rootDir": "/packages/my-app/" + "rootDir": "packages/my-app/" } } } ``` -`rootDir` can be a path (relative or absolute), a glob (i.e. `"/packages/*/"`), or an array of paths and/or globs. +`rootDir` can be a path (relative or absolute), a glob (i.e. `"packages/*/"`), or an array of paths and/or globs. -## Linting Custom Directories +## Linting Custom Directories and Files By default, Next.js will run ESLint for all files in the `pages/`, `components/`, and `lib/` directories. However, you can specify which directories using the `dirs` option in the `eslint` config in `next.config.js` for production builds: @@ -133,10 +134,10 @@ module.exports = { } ``` -Similarly, the `--dir` flag can be used for `next lint`: +Similarly, the `--dir` and `--file` flags can be used for `next lint` to lint specific directories and files: ```bash -next lint --dir pages --dir utils +next lint --dir pages --dir utils --file bar.js ``` ## Caching @@ -175,16 +176,45 @@ The `next/core-web-vitals` rule set is enabled when `next lint` is run for the f > The `next/core-web-vitals` entry point is automatically included for new applications built with [Create Next App](/docs/api-reference/create-next-app.md). -## Usage with Prettier +## Usage With Other Tools + +### Prettier ESLint also contains code formatting rules, which can conflict with your existing [Prettier](https://prettier.io/) setup. We recommend including [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) in your ESLint config to make ESLint and Prettier work together. +First, install the dependency: + +```bash +npm install --save-dev eslint-config-prettier +# or +yarn add --dev eslint-config-prettier +``` + +Then, add `prettier` to your existing ESLint config: + ```json { "extends": ["next", "prettier"] } ``` +### lint-staged + +If you would like to use `next lint` with [lint-staged](https://github.com/okonet/lint-staged) to run the linter on staged git files, you'll have to add the following to the `.lintstagedrc.js` file in the root of your project in order to specify usage of the `--file` flag. + +```js +const path = require('path') + +const buildEslintCommand = (filenames) => + `next lint --fix --file ${filenames + .map((f) => path.relative(process.cwd(), f)) + .join(' --file ')}` + +module.exports = { + '*.{js,jsx,ts,tsx}': [buildEslintCommand], +} +``` + ## Migrating Existing Config ### Recommended Plugin Ruleset diff --git a/docs/basic-features/fast-refresh.md b/docs/basic-features/fast-refresh.md index 3b5678ba7e60..e12dd9749b26 100644 --- a/docs/basic-features/fast-refresh.md +++ b/docs/basic-features/fast-refresh.md @@ -74,9 +74,9 @@ local state being reset on every edit to a file: and Hooks preserve state). - The file you're editing might have _other_ exports in addition to a React component. -- Sometimes, a file would export the result of calling higher-order component +- Sometimes, a file would export the result of calling a higher-order component like `HOC(WrappedComponent)`. If the returned component is a - class, state will be reset. + class, its state will be reset. - Anonymous arrow functions like `export default () => <div />;` cause Fast Refresh to not preserve local component state. For large codebases you can use our [`name-default-component` codemod](/docs/advanced-features/codemods.md#name-default-component). As more of your codebase moves to function components and Hooks, you can expect diff --git a/docs/basic-features/font-optimization.md b/docs/basic-features/font-optimization.md index 7f9d06a1557b..a36b731b29b4 100644 --- a/docs/basic-features/font-optimization.md +++ b/docs/basic-features/font-optimization.md @@ -11,12 +11,12 @@ By default, Next.js will automatically inline font CSS at build time, eliminatin ```js // Before <link - href="https://fonts.googleapis.com/css2?family=Inter" + href="https://fonts.googleapis.com/css2?family=Inter&display=optional" rel="stylesheet" /> // After -<style data-href="https://fonts.googleapis.com/css2?family=Inter"> +<style data-href="https://fonts.googleapis.com/css2?family=Inter&display=optional"> @font-face{font-family:'Inter';font-style:normal... </style> ``` @@ -78,6 +78,8 @@ Automatic Webfont Optimization currently supports Google Fonts and Typekit with See [Google Font Display](https://nextjs.org/docs/messages/google-font-display) for more information. +> **Note**: Font Optimization does not currently support self-hosted fonts. + ## Disabling Optimization If you do not want Next.js to optimize your fonts, you can opt-out. diff --git a/docs/basic-features/image-optimization.md b/docs/basic-features/image-optimization.md index 7c8e28074cd7..0c7cb5a6a292 100644 --- a/docs/basic-features/image-optimization.md +++ b/docs/basic-features/image-optimization.md @@ -11,67 +11,75 @@ description: Next.js supports built-in image optimization, as well as third part </ul> </details> -Since version **10.0.0**, Next.js has a built-in Image Component and Automatic Image Optimization. +The Next.js Image component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web. It includes a variety of built-in performance optimizations to help you achieve good [Core Web Vitals](https://nextjs.org/learn/seo/web-performance). These scores are an important measurement of user experience on your website, and are [factored into Google's search rankings](https://nextjs.org/learn/seo/web-performance/seo-impact). -The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web. +Some of the optimizations built into the Image component include: -The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. +- **Improved Performance:** Always serve correctly sized image for each device, using modern image formats +- **Visual Stability:** Prevent [Cumulative Layout Shift](https://nextjs.org/learn/seo/web-performance/cls) automatically +- **Faster Page Loads:** Images are only loaded when they enter the viewport, with optional blur-up placeholders +- **Asset Flexibility:** On-demand image resizing, even for images stored on remote servers -Automatic Image Optimization works with any image source. Even if the image is hosted by an external data source, like a CMS, it can still be optimized. +## Using the Image Component -Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Unlike static site generators and static-only solutions, your build times aren't increased, whether shipping 10 images or 10 million images. +To add an image to your application, import the [`next/image`](/docs/api-reference/next/image.md) component: -Images are lazy loaded by default. That means your page speed isn't penalized for images outside the viewport. Images load as they are scrolled into viewport. +```jsx +import Image from 'next/image' +``` -Images are always rendered in such a way as to avoid [Cumulative Layout Shift](https://web.dev/cls/), a [Core Web Vital](https://web.dev/vitals/) that Google [uses in search ranking](https://developers.google.com/search/blog/2020/05/evaluating-page-experience). +Now, you can define the `src` for your image (either local or remote). -## Image Component +### Local Images -To add an image to your application, import the [`next/image`](/docs/api-reference/next/image.md) component: +To use a local image, `import` your `.jpg`, `.png`, or `.webp` files: ```jsx +import profilePic from '../public/me.png' +``` + +Dynamic `await import()` or `require()` are _not_ supported. The `import` must be static so it can be analyzed at build time. + +Next.js will automatically determine 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. + +```js import Image from 'next/image' +import profilePic from '../public/me.png' function Home() { return ( <> <h1>My Homepage</h1> <Image - src="/me.png" + src={profilePic} alt="Picture of the author" - width={500} - height={500} + // width={500} automatically provided + // height={500} automatically provided + // blurDataURL="data:..." automatically provided + // placeholder="blur" // Optional blur-up while loading /> <p>Welcome to my homepage!</p> </> ) } - -export default Home ``` -## Image Imports - -You can `import` images that live in your project. (Note that `require` is not supported—only `import`.) +### Remote Images -With direct `import`s, `width`, `height`, and `blurDataURL` will be automatically provided to the image component. Alt text is still needed separately. +To use a remote image, the `src` property should be a URL string, which can be [relative](#loaders) or [absolute](#domains). Because Next.js does not have access to remote files during the build process, you'll need to provide the [`width`](/docs/api-reference/next/image.md#width), [`height`](/docs/api-reference/next/image.md#height) and optional [`blurDataURL`](/docs/api-reference/next/image.md#blurdataurl) props manually: -```js +```jsx import Image from 'next/image' -import profilePic from '../public/me.png' -function Home() { +export default function Home() { return ( <> <h1>My Homepage</h1> <Image - src={profilePic} + src="/me.png" alt="Picture of the author" - // width={500} automatically provided - // height={500} automatically provided - // blurDataURL="data:..." automatically provided - // Optionally allows to add a blurred version of the image while loading - // placeholder="blur" + width={500} + height={500} /> <p>Welcome to my homepage!</p> </> @@ -79,132 +87,131 @@ function Home() { } ``` -For dynamic or remote images, you'll have to provide [`width`](/docs/api-reference/next/image#width), [`height`](/docs/api-reference/next/image#height) and [`blurDataURL`](/docs/api-reference/next/image#blurdataurl) manually. - -## Properties - -[View all properties](/docs/api-reference/next/image.md) available to the `next/image` component. - -## Configuration - -In addition to [using properties](/docs/api-reference/next/image.md) available to the `next/image` component, you can optionally configure Image Optimization for more advanced use cases via `next.config.js`. +> Learn more about the [sizing requirements](#image-sizing) in `next/image`. ### Domains -To enable Image Optimization for images hosted on an external website, use an absolute url for the Image `src` and specify which -`domains` are allowed to be optimized. This is needed to ensure that external urls can't be abused. When `loader` is set to an external image service, this option is ignored. +Sometimes you may want to access 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`. + +To protect your application from malicious users, you must define a list of remote domains that you intend to access this way. This is configured in your `next.config.js` file, as shown below: ```js module.exports = { images: { - domains: ['example.com'], + domains: ['example.com', 'example2.com'], }, } ``` -### Loader +### Loaders -If you want to use a cloud provider to optimize images instead of using the Next.js' built-in Image Optimization, you can configure the loader and path prefix. This allows you to use relative urls for the Image `src` and automatically generate the correct absolute url for your provider. +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 `next/image` [loader](/docs/api-reference/next/image.md#loader) architecture. -```js -module.exports = { - images: { - loader: 'imgix', - path: 'https://example.com/myaccount/', - }, -} -``` +A loader is a function that generates the URLs for your image. It appends a root domain to your 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 use one of the [built-in loaders](/docs/api-reference/next/image.md#built-in-loaders) or write your own with a few lines of JavaScript. -The following Image Optimization cloud providers are included: +Loaders can be defined per-image, or at the application level. -- [Vercel](https://vercel.com): Works automatically when you deploy on Vercel, no configuration necessary. [Learn more](https://vercel.com/docs/next.js/image-optimization) -- [Imgix](https://www.imgix.com): `loader: 'imgix'` -- [Cloudinary](https://cloudinary.com): `loader: 'cloudinary'` -- [Akamai](https://www.akamai.com): `loader: 'akamai'` -- Custom: `loader: 'custom'` use a custom cloud provider by implementing the [`loader`](/docs/api-reference/next/image.md#loader) prop on the `next/image` component -- Default: Works automatically with `next dev`, `next start`, or a custom server +### Priority -If you need a different provider, you can use the [`loader`](/docs/api-reference/next/image.md#loader) prop with `next/image`. +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 `next/image` component's default loader is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). However, other loader options will work. +The 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 `<Image>` without the `priority` property. -> The `next/image` component's default loader uses [`squoosh`](https://www.npmjs.com/package/@squoosh/lib) because it is quick to install and suitable for a development environment. When using `next start` in your production environment, it is strongly recommended that you install [`sharp`](https://www.npmjs.com/package/sharp) by running `yarn add sharp` in your project directory. This is not necessary for Vercel deployments, as `sharp` is installed automatically. +Once you've identified the LCP image, you can add the property like this: -## Caching +```jsx +import Image from 'next/image' -The following describes the caching algorithm for the default [loader](#loader). For all other loaders, please refer to your cloud provider's documentation. +export default function Home() { + return ( + <> + <h1>My Homepage</h1> + <Image + src="/me.png" + alt="Picture of the author" + width={500} + height={500} + priority + /> + <p>Welcome to my homepage!</p> + </> + ) +} +``` -Images are optimized dynamically upon request and stored in the `<distDir>/cache/images` directory. The optimized image file will be served for subsequent requests until the expiration is reached. When a request is made that matches a cached but expired file, the cached file is deleted before generating a new optimized image and caching the new file. +See more about priority in the [`next/image` component documentation](/docs/api-reference/next/image.md#priority). -The expiration (or rather Max Age) is defined by the upstream server's `Cache-Control` header. +## Image Sizing -If `s-maxage` is found in `Cache-Control`, it is used. If no `s-maxage` is found, then `max-age` is used. If no `max-age` is found, then [`minimumCacheTTL`](#minimum-cache-ttl) is used. +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. -You can configure [`minimumCacheTTL`](#minimum-cache-ttl) to increase the cache duration when the upstream image does not include `max-age`. +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: -You can also configure [`deviceSizes`](#device-sizes) and [`imageSizes`](#device-sizes) to reduce the total number of possible generated images. +1. Automatically, using a [static import](#local-images) +2. Explicitly, by including a `height` **and** `width` property +3. Implicitly, by using `layout="fill"` which causes the image to expand to fill its parent element. -## Advanced +> ### 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 `layout='fill'`** +> +> The `fill` layout mode allows your image to be sized by its parent element. Consider using CSS to give the image's parent element space on the page, then using the [`objectFit property`](/docs/api-reference/next/image.md#objectfit) with `fill`, `contain`, or `cover`, along with the [`objectPosition property`](/docs/api-reference/next/image.md#objectposition) 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. -The following configuration is for advanced use cases and is usually not necessary. If you choose to configure the properties below, you will override any changes to the Next.js defaults in future updates. +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 `<img>` elements. -### Device Sizes +## Styling -In some cases, where you know the expected device widths from the users of your website, you can specify a list of device width breakpoints using the `deviceSizes` property. These widths are used when the [`next/image`](/docs/api-reference/next/image.md) component uses `layout="responsive"` or `layout="fill"` so that the correct image is served for the device visiting your website. +Styling the Image component is not that different from styling a normal `<img>` element, but there are a few guidelines to keep in mind: -If no configuration is provided, the default below is used. +**Pick the correct layout mode** -```js -module.exports = { - images: { - deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], - }, -} -``` +The image component has several different [layout modes](/docs/api-reference/next/image.md#layout) that define how it is sized on the page. If the styling of your image isn't turning out the way you want, consider experimenting with other layout modes. -### Image Sizes +**Target the image with className, not based on DOM structure** -You can specify a list of image widths using the `imageSizes` property. These widths should be different (usually smaller) than the widths defined in `deviceSizes` because the arrays will be concatenated. These widths are used when the [`next/image`](/docs/api-reference/next/image.md) component uses `layout="fixed"` or `layout="intrinsic"`. +Regardless of the layout mode used, the Image component will have a consistent DOM structure of one `<img>` tag wrapped by exactly one `<span>`. For some modes, it may also have a sibling `<span>` for spacing. These additional `<span>` elements are critical to allow the component to prevent layout shifts. -If no configuration is provided, the default below is used. +The recommended way to style the inner `<img>` is to set the `className` prop on the Image component to the value of an imported [CSS Module](/docs/basic-features/built-in-css-support.md#adding-component-level-css). The value of `className` will be automatically applied to the underlying `<img>` element. -```js -module.exports = { - images: { - imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], - }, -} -``` +Alternatively, you can import a [global stylesheet](/docs/basic-features/built-in-css-support#adding-a-global-stylesheet) and manually set the `className` prop to the same name used in the global stylesheet. -### Minimum Cache TTL +You cannot use [styled-jsx](/docs/basic-features/built-in-css-support.md#css-in-js) because it's scoped to the current component. -You can configure the time to live (TTL) in seconds for cached optimized images. In many cases, its better to use a [Static Image Import](#image-Imports) which will handle hashing file contents and caching the file forever. +You cannot use the `style` prop because the `<Image>` component does not pass it through to the underlying `<img>`. -```js -module.exports = { - images: { - minimumCacheTTL: 60, - }, -} -``` +**When using `layout='fill'`, the parent element must have `position: relative`** -If you need to add a `Cache-Control` header for the browser (not recommended), you can configure [`headers`](/docs/api-reference/next.config.js/headers) on the upstream image e.g. `/some-asset.jpg` not `/_next/image` itself. +This is necessary for the proper rendering of the image element in that layout mode. -### Disable Static Imports +**When using `layout='responsive'`, the parent element must have `display: block`** -The default behavior allows you to import static files such as `import icon from './icon.png` and then pass that to the `src` property. +This is the default for `<div>` elements but should be specified otherwise. -In some cases, you may wish to disable this feature if it conflicts with other plugins that expect the import to behave differently. +## Properties -You can disable static image imports with the following configuration below. +[**View all properties available to the `next/image` component.**](/docs/api-reference/next/image.md) -```js -module.exports = { - images: { - disableStaticImages: true, - }, -} -``` +### Styling Examples + +For examples of the Image component used with the various fill modes, see the [Image component example app](https://image-component.nextjs.gallery/). + +## Configuration + +The `next/image` component and Next.js Image Optimization API can be configured in the [`next.config.js` file](/docs/api-reference/next.config.js/introduction.md). These configurations allow you to [enable remote domains](/docs/api-reference/next/image.md#domains), [define custom image breakpoints](/docs/api-reference/next/image.md#device-sizes), [change caching behavior](/docs/api-reference/next/image.md#caching-behavior) and more. + +[**Read the full image configuration documentation for more information.**](/docs/api-reference/next/image.md#configuration-options) ## Related diff --git a/docs/basic-features/pages.md b/docs/basic-features/pages.md index 7efbfc1382f1..76bda662b3f1 100644 --- a/docs/basic-features/pages.md +++ b/docs/basic-features/pages.md @@ -41,7 +41,7 @@ Importantly, Next.js lets you **choose** which pre-rendering form you'd like to We **recommend** using **Static Generation** over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN with no extra configuration to boost performance. However, in some cases, Server-side Rendering might be the only option. -You can also use **Client-side Rendering** along with Static Generation or Server-side Rendering. That means some parts of a page can be rendered entirely by client side JavaScript. To learn more, take a look at the [Data Fetching](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side) documentation. +You can also use **Client-side Rendering** along with Static Generation or Server-side Rendering. That means some parts of a page can be rendered entirely by client side JavaScript. To learn more, take a look at the [Data Fetching](/docs/basic-features/data-fetching/client-side.md) documentation. ## Static Generation (Recommended) @@ -63,7 +63,8 @@ You can also use **Client-side Rendering** along with Static Generation or Serve <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app/">Demo</a>)</li> - <li><a href="https://static-tweet.vercel.app/">Static Tweet Demo</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-builder-io">Builder.io Example</a> (<a href="https://cms-builder-io.vercel.app/">Demo</a>)</li> + <li><a href="https://static-tweet.vercel.app/">Static Tweet (Demo)</a></li> </ul> </details> @@ -137,7 +138,7 @@ export async function getStaticProps() { export default Blog ``` -To learn more about how `getStaticProps` works, check out the [Data Fetching documentation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). +To learn more about how `getStaticProps` works, check out the [Data Fetching documentation](/docs/basic-features/data-fetching/get-static-props.md). #### Scenario 2: Your page paths depend on external data @@ -196,7 +197,7 @@ export async function getStaticProps({ params }) { export default Post ``` -To learn more about how `getStaticPaths` works, check out the [Data Fetching documentation](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation). +To learn more about how `getStaticPaths` works, check out the [Data Fetching documentation](/docs/basic-features/data-fetching/get-static-paths.md). ### When should I use Static Generation? @@ -205,7 +206,7 @@ We recommend using **Static Generation** (with and without data) whenever possib You can use Static Generation for many types of pages, including: - Marketing pages -- Blog posts +- Blog posts and portfolios - E-commerce product listings - Help and documentation @@ -215,7 +216,7 @@ On the other hand, Static Generation is **not** a good idea if you cannot pre-re In cases like this, you can do one of the following: -- Use Static Generation with **Client-side Rendering:** You can skip pre-rendering some parts of a page and then use client-side JavaScript to populate them. To learn more about this approach, check out the [Data Fetching documentation](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side). +- Use Static Generation with **Client-side Rendering:** You can skip pre-rendering some parts of a page and then use client-side JavaScript to populate them. To learn more about this approach, check out the [Data Fetching documentation](/docs/basic-features/data-fetching/client-side.md). - Use **Server-Side Rendering:** Next.js pre-renders a page on each request. It will be slower because the page cannot be cached by a CDN, but the pre-rendered page will always be up-to-date. We'll talk about this approach below. ## Server-side Rendering @@ -248,7 +249,7 @@ export default Page As you can see, `getServerSideProps` is similar to `getStaticProps`, but the difference is that `getServerSideProps` is run on every request instead of on build time. -To learn more about how `getServerSideProps` works, check out our [Data Fetching documentation](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) +To learn more about how `getServerSideProps` works, check out our [Data Fetching documentation](/docs/basic-features/data-fetching/get-server-side-props.md) ## Summary @@ -262,7 +263,7 @@ We've discussed two forms of pre-rendering for Next.js. We recommend you to read the following sections next: <div class="card"> - <a href="/docs/basic-features/data-fetching.md"> + <a href="/docs/basic-features/data-fetching/overview.md"> <b>Data Fetching:</b> <small>Learn more about data fetching in Next.js.</small> </a> diff --git a/docs/basic-features/script.md b/docs/basic-features/script.md index ef688170332e..e8642346389e 100644 --- a/docs/basic-features/script.md +++ b/docs/basic-features/script.md @@ -4,6 +4,18 @@ description: Next.js helps you optimize loading third-party scripts with the bui # Script Component +<details> + <summary><b>Examples</b></summary> + <ul> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/script-component">Script Component</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-tag-manager">Google Tag Manager</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-analytics">Google Analytics</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel">Facebook Pixel</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-clerk">Clerk</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-segment-analytics">Segment Analytics</a></li> + </ul> +</details> + <details> <summary><b>Version History</b></summary> @@ -13,112 +25,168 @@ description: Next.js helps you optimize loading third-party scripts with the bui </details> -The Next.js Script component enables developers to set the loading priority of third-party scripts to save developer time and improve loading performance. +The Next.js Script component, [`next/script`](/docs/api-reference/next/script.md), is an extension of the HTML `<script>` element. It enables developers to set the loading priority of third-party scripts anywhere in their application without needing to append directly to `next/head`, saving developer time while improving loading performance. -Websites often need third parties for things like analytics, ads, customer support widgets, and consent management. However, these scripts tend to be heavy on loading performance and can drag down the user experience. Developers often struggle to decide where to place them in an application for optimal loading. +```jsx +import Script from 'next/script' + +export default function Home() { + return ( + <> + <Script src="https://www.google-analytics.com/analytics.js" /> + </> + ) +} +``` -With `next/script`, you can define the `strategy` property and Next.js will optimize loading for the script: +## Overview -- `beforeInteractive`: For critical scripts that need to be fetched and executed **before** the page is interactive, such as bot detection and consent management. These scripts are injected into the initial HTML from the server and run before self-bundled JavaScript is executed. -- `afterInteractive` (**default**): For scripts that can fetch and execute **after** the page is interactive, such as tag managers and analytics. These scripts are injected on the client-side and will run after hydration. -- `lazyOnload` For scripts that can wait to load during idle time, such as chat support and social media widgets. +Websites often use third-party scripts to include different types of functionality into their site, such as analytics, ads, customer support widgets, and consent management. However, this can introduce problems that impact both user and developer experience: -> **Note:** -> -> - `<Script>` supports inline scripts with `afterInteractive` and `lazyOnload` strategy. -> - Inline scripts wrapped with `<Script>` _require an `id` attribute to be defined_ to track and optimize the script. +- Some third-party scripts are heavy on loading performance and can drag down the user experience, especially if they are render-blocking and delay any page content from loading +- Developers often struggle to decide where to place third-party scripts in an application to ensure optimal loading + +The Script component makes it easier for developers to place a third-party script anywhere in their application while taking care of optimizing its loading strategy. ## Usage -Previously, you needed to define `script` tags inside the `Head` of your Next.js page. +To add a third-party script to your application, import the `next/script` component: -```js -// Before +```jsx +import Script from 'next/script' +``` -// pages/index.js -import Head from 'next/head' +### Strategy -export default function Home() { - return ( - <> - <Head> - <script async src="https://www.google-analytics.com/analytics.js" /> - </Head> - </> - ) -} +With `next/script`, you decide when to load your third-party script by using the `strategy` property: + +```jsx +<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" /> ``` -Now, you use `next/script` in the body of your Next.js page. It has client-side functionality that decides when and how to load the remote script based on the `strategy`. +There are three different loading strategies that can be used: -> **Note:** -> -> - `next/script` **must not** be placed in either a `next/head` component or in `pages/_document.js`. +- `beforeInteractive`: Load before the page is interactive +- `afterInteractive`: (**default**): Load immediately after the page becomes interactive +- `lazyOnload`: Load during idle time -```js -// After +#### beforeInteractive -// pages/index.js -import Script from 'next/script' +Scripts that load with the `beforeInteractive` strategy are injected into the initial HTML from the server and run before self-bundled JavaScript is executed. This strategy should be used for any critical scripts that need to be fetched and executed before the page is interactive. -export default function Home() { - return ( - <> - <Script src="https://www.google-analytics.com/analytics.js" /> - </> - ) -} +```jsx +<Script + src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js" + strategy="beforeInteractive" +/> ``` -## Examples +Examples of scripts that should be loaded as soon as possible with this strategy include: -### Loading Polyfills +- Bot detectors +- Cookie consent managers -```js -import Script from 'next/script' +#### afterInteractive -export default function Home() { - return ( - <> - <Script - src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver" - strategy="beforeInteractive" - /> - </> - ) -} +Scripts that use the `afterInteractive` strategy are injected client-side and will run after Next.js hydrates the page. This strategy should be used for scripts that do not need to load as soon as possible and can be fetched and executed immediately after the page is interactive. + +```jsx +<Script + strategy="afterInteractive" + dangerouslySetInnerHTML={{ + __html: ` + (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': + new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], + j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= + 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); + })(window,document,'script','dataLayer', 'GTM-XXXXXX'); + `, + }} +/> ``` -### Lazy-Loading +Examples of scripts that are good candidates to load immediately after the page becomes interactive include: + +- Tag managers +- Analytics + +#### lazyOnload -```js +Scripts that use the `lazyOnload` strategy are loaded late after all resources have been fetched and during idle time. This strategy should be used for background or low priority scripts that do not need to load before or immediately after a page becomes interactive. + +```jsx +<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" /> +``` + +Examples of scripts that do not need to load immediately and can be lazy-loaded include: + +- Chat support plugins +- Social media widgets + +### Inline Scripts + +Inline scripts, or scripts not loaded from an external file, are also supported by the Script component. They can be written by placing the JavaScript within curly braces: + +```jsx +<Script id="show-banner" strategy="lazyOnload"> + {`document.getElementById('banner').classList.remove('hidden')`} +</Script> +``` + +Or by using the `dangerouslySetInnerHTML` property: + +```jsx +<Script + id="show-banner" + dangerouslySetInnerHTML={{ + __html: `document.getElementById('banner').classList.remove('hidden')`, + }} +/> +``` + +There are two limitations to be aware of when using the Script component for inline scripts: + +- Only the `afterInteractive` and `lazyOnload` strategies can be used. The `beforeInteractive` loading strategy injects the contents of an external script into the initial HTML response. Inline scripts already do this, which is why **the `beforeInteractive` strategy cannot be used with inline scripts.** +- An `id` attribute must be defined in order for Next.js to track and optimize the script + +### Executing Code After Loading (`onLoad`) + +Some third-party scripts require users to run JavaScript code after the script has finished loading in order to instantiate content or call a function. If you are loading a script with either `beforeInteractive` or `afterInteractive` as a loading strategy, you can execute code after it has loaded using the `onLoad` property: + +```jsx +import { useState } from 'react' import Script from 'next/script' export default function Home() { + const [stripe, setStripe] = useState(null) + return ( <> <Script - src="https://connect.facebook.net/en_US/sdk.js" - strategy="lazyOnload" + id="stripe-js" + src="https://js.stripe.com/v3/" + onLoad={() => { + setStripe({ stripe: window.Stripe('pk_test_12345') }) + }} /> </> ) } ``` -### Executing Code After Loading (`onLoad`) +Sometimes it is helpful to catch when a script fails to load. These errors can be handled with the `onError` property: -```js +```jsx import Script from 'next/script' export default function Home() { return ( <> <Script - id="stripe-js" - src="https://js.stripe.com/v3/" - onLoad={() => { - this.setState({ stripe: window.Stripe('pk_test_12345') }) + id="will-fail" + src="https://example.com/non-existant-script.js" + onError={(e) => { + console.error('Script failed to load', e) }} /> </> @@ -126,28 +194,11 @@ export default function Home() { } ``` -### Inline Scripts - -```js -import Script from 'next/script' - -<Script id="show-banner" strategy="lazyOnload"> - {`document.getElementById('banner').removeClass('hidden')`} -</Script> - -// or - -<Script - id="show-banner" - dangerouslySetInnerHTML={{ - __html: `document.getElementById('banner').removeClass('hidden')` - }} -/> -``` +### Additional Attributes -### Forwarding Attributes +There are many DOM attributes that can be assigned to a `<script>` element that are not used by the Script component, like [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) or [custom data attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*). Including any additional attributes will automatically forward it to the final, optimized `<script>` element that is outputted to the page. -```js +```jsx import Script from 'next/script' export default function Home() { diff --git a/docs/basic-features/typescript.md b/docs/basic-features/typescript.md index 8eeab64ceafe..db23d29dad07 100644 --- a/docs/basic-features/typescript.md +++ b/docs/basic-features/typescript.md @@ -19,7 +19,7 @@ experience out of the box, similar to an IDE. You can create a TypeScript project with [`create-next-app`](https://nextjs.org/docs/api-reference/create-next-app) using the `--ts, --typescript` flag like so: ``` -npx create-next-app --ts +npx create-next-app@latest --ts # or yarn create next-app --typescript ``` @@ -35,7 +35,11 @@ touch tsconfig.json Next.js will automatically configure this file with default values. Providing your own `tsconfig.json` with custom [compiler options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) is also supported. -> Next.js uses Babel to handle TypeScript, which has some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats), and some [compiler options are handled differently](https://babeljs.io/docs/en/babel-plugin-transform-typescript#typescript-compiler-options). +You can also provide a relative path to a tsconfig.json file by setting `typescript.tsconfigPath` prop inside your `next.config.js` file. + +Starting in `v12.0.0`, Next.js uses [SWC](https://nextjs.org/docs/advanced-features/compiler) by default to compile TypeScript and TSX for faster builds. + +> Next.js will use Babel to handle TypeScript if `.babelrc` is present. This has some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats) and some [compiler options are handled differently](https://babeljs.io/docs/en/babel-plugin-transform-typescript#typescript-compiler-options). Then, run `next` (normally `npm run dev` or `yarn dev`) and Next.js will guide you through the installation of the required packages to finish the setup: @@ -44,14 +48,14 @@ npm run dev # You'll see instructions like these: # -# Please install typescript, @types/react, and @types/node by running: +# Please install TypeScript, @types/react, and @types/node by running: # # yarn add --dev typescript @types/react @types/node # # ... ``` -You're now ready to start converting files from `.js` to `.tsx` and leveraging the benefits of TypeScript!. +You're now ready to start converting files from `.js` to `.tsx` and leveraging the benefits of TypeScript! > A file named `next-env.d.ts` will be created in the root of your project. This file ensures Next.js types are picked up by the TypeScript compiler. **You cannot remove it or edit it** as it can change at any time. @@ -83,7 +87,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { } ``` -> If you're using `getInitialProps`, you can [follow the directions on this page](/docs/api-reference/data-fetching/getInitialProps.md#typescript). +> If you're using `getInitialProps`, you can [follow the directions on this page](/docs/api-reference/data-fetching/get-initial-props.md#typescript). ## API Routes diff --git a/docs/deployment.md b/docs/deployment.md index b4c30a380616..f6c14f12436c 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,144 +1,108 @@ --- -description: Deploy your Next.js app to production with Vercel and other hosting options. +description: Learn how to deploy your Next.js app to production, either managed or self-hosted. --- # Deployment -## Vercel (Recommended) +Congratulations, you are ready to deploy your Next.js application to production. This document will show how to deploy either managed or self-hosted using the [Next.js Build API](#nextjs-build-api). -The easiest way to deploy Next.js to production is to use the **[Vercel platform](https://vercel.com)** from the creators of Next.js. [Vercel](https://vercel.com) is a cloud platform for static sites, hybrid apps, and Serverless Functions. +## Next.js Build API -### Getting started +`next build` generates an optimized version of your application for production. This standard output includes: -If you haven’t already done so, push your Next.js app to a Git provider of your choice: [GitHub](https://github.com/), [GitLab](https://gitlab.com/), or [BitBucket](https://bitbucket.org/). Your repository can be private or public. +- HTML files for pages using `getStaticProps` or [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) +- CSS files for global styles or for individually scoped styles +- JavaScript for pre-rendering dynamic content from the Next.js server +- JavaScript for interactivity on the client-side through React -Then, follow these steps: +This output is generated inside the `.next` folder: -1. [Sign up to Vercel](https://vercel.com/signup) (no credit card is required). -2. After signing up, you’ll arrive on the [“Import Project”](https://vercel.com/new) page. Under “From Git Repository”, choose the Git provider you use and set up an integration. (Instructions: [GitHub](https://vercel.com/docs/git/vercel-for-github) / [GitLab](https://vercel.com/docs/git/vercel-for-gitlab) / [BitBucket](https://vercel.com/docs/git/vercel-for-bitbucket)). -3. Once that’s set up, click “Import Project From …” and import your Next.js app. It auto-detects that your app is using Next.js and sets up the build configuration for you. No need to change anything — everything should work fine! -4. After importing, it’ll deploy your Next.js app and provide you with a deployment URL. Click “Visit” to see your app in production. +- `.next/static/chunks/pages` – Each JavaScript file inside this folder relates to the route with the same name. For example, `.next/static/chunks/pages/about.js` would be the JavaScript file loaded when viewing the `/about` route in your application +- `.next/static/media` – Statically imported images from `next/image` are hashed and copied here +- `.next/static/css` – Global CSS files for all pages in your application +- `.next/server/pages` – The HTML and JavaScript entry points prerendered from the server. The `.nft.json` files are created when [Output File Tracing](/docs/advanced-features/output-file-tracing.md) is enabled and contain all the file paths that depend on a given page. +- `.next/server/chunks` – Shared JavaScript chunks used in multiple places throughout your application +- `.next/cache` – Output for the build cache and cached images, responses, and pages from the Next.js server. Using a cache helps decrease build times and improve performance of loading images -Congratulations! You’ve deployed your Next.js app! If you have questions, take a look at the [Vercel documentation](https://vercel.com/docs). +All JavaScript code inside `.next` has been **compiled** and browser bundles have been **minified** to help achieve the best performance and support [all modern browsers](/docs/basic-features/supported-browsers-features.md). -> If you’re using a [custom server](/docs/advanced-features/custom-server.md), we strongly recommend migrating away from it (for example, by using [dynamic routing](/docs/routing/dynamic-routes.md)). If you cannot migrate, consider [other hosting options](#other-hosting-options). +## Managed Next.js with Vercel -### DPS: Develop, Preview, Ship +[Vercel](https://vercel.com?utm_source=github.com&utm_medium=referral&utm_campaign=deployment) is the fastest way to deploy your Next.js application with zero configuration. -Let’s talk about the workflow we recommend using. [Vercel](https://vercel.com) supports what we call the **DPS** workflow: **D**evelop, **P**review, and **S**hip: +When deploying to Vercel, the platform [automatically detects Next.js](https://vercel.com/solutions/nextjs?utm_source=github.com&utm_medium=referral&utm_campaign=deployment), runs `next build`, and optimizes the build output for you, including: -- **Develop:** Write code in Next.js. Keep the development server running and take advantage of [React Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh). -- **Preview:** Every time you push changes to a branch on GitHub / GitLab / BitBucket, Vercel automatically creates a new deployment with a unique URL. You can view them on GitHub when you open a pull request, or under “Preview Deployments” on your project page on Vercel. [Learn more about it here](https://vercel.com/features/deployment-previews). -- **Ship:** When you’re ready to ship, merge the pull request to your default branch (e.g. `main`). Vercel will automatically create a production deployment. +- Persisting cached assets across deployments if unchanged +- [Immutable deployments](https://vercel.com/features/previews) with a unique URL for every commit +- [Pages](/docs/basic-features/pages.md) are automatically statically optimized, if possible +- Assets (JavaScript, CSS, images, fonts) are compressed and served from a [Global Edge Network](https://vercel.com/features/infrastructure) +- [API Routes](/docs/api-routes/introduction.md) are automatically optimized as isolated [Serverless Functions](https://vercel.com/features/infrastructure) that can scale infinitely +- [Middleware](/docs/middleware.md) are automatically optimized as [Edge Functions](https://vercel.com/features/edge-functions) that have zero cold starts and boot instantly -By using the DPS workflow, in addition to doing _code reviews_, you can do _deployment previews_. Each deployment creates a unique URL that can be shared or used for integration tests. +In addition, Vercel provides features like: -### Optimized for Next.js +- Automatic performance monitoring with [Next.js Analytics](https://vercel.com/analytics) +- Automatic HTTPS and SSL certificates +- Automatic CI/CD (through GitHub, GitLab, Bitbucket, etc.) +- Support for [Environment Variables](https://vercel.com/docs/environment-variables) +- Support for [Custom Domains](https://vercel.com/docs/custom-domains) +- Support for [Image Optimization](/docs/basic-features/image-optimization.md) with `next/image` +- Instant global deployments via `git push` -[Vercel](https://vercel.com) is made by the creators of Next.js and has first-class support for Next.js. +[Deploy a Next.js application to Vercel](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/hello-world&project-name=hello-world&repository-name=hello-world&utm_source=github.com&utm_medium=referral&utm_campaign=deployment) for free to try it out. -For example, the [hybrid pages](/docs/basic-features/pages.md) approach is fully supported out of the box. +## Self-Hosting -- Every page can either use [Static Generation](/docs/basic-features/pages.md#static-generation) or [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering). -- Pages that use [Static Generation](/docs/basic-features/pages.md#static-generation) and assets (JS, CSS, images, fonts, etc) will automatically be served from [Vercel's Edge Network](https://vercel.com/docs/edge-network/overview), which is blazingly fast. -- Pages that use [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering) and [API routes](/docs/api-routes/introduction.md) will automatically become isolated Serverless Functions. This allows page rendering and API requests to scale infinitely. - -### Custom Domains, Environment Variables, Automatic HTTPS, and more - -- **Custom Domains:** Once deployed on [Vercel](https://vercel.com), you can assign a custom domain to your Next.js app. Take a look at [our documentation here](https://vercel.com/docs/custom-domains). -- **Environment Variables:** You can also set environment variables on Vercel. Take a look at [our documentation here](https://vercel.com/docs/environment-variables). You can then [use those environment variables](/docs/api-reference/next.config.js/environment-variables.md) in your Next.js app. -- **Automatic HTTPS:** HTTPS is enabled by default (including custom domains) and doesn't require extra configuration. We auto-renew SSL certificates. -- **More:** [Read our documentation](https://vercel.com/docs) to learn more about the Vercel platform. - -## Automatic Updates - -When you deploy your Next.js application, you want to see the latest version without needing to reload. - -Next.js will automatically load the latest version of your application in the background when routing. For client-side navigation, `next/link` will temporarily function as a normal `<a>` tag. - -**Note:** If a new page (with an old version) has already been prefetched by `next/link`, Next.js will use the old version. Then, after either a full page refresh or multiple client-side page transitions, Next.js will show the latest version. - -## Other hosting options +You can self-host Next.js with support for all features using Node.js or Docker. You can also do a Static HTML Export, which [has some limitations](/docs/advanced-features/static-html-export.md). ### Node.js Server -Next.js can be deployed to any hosting provider that supports Node.js. This is the approach you should take if you’re using a [custom server](/docs/advanced-features/custom-server.md). +Next.js can be deployed to any hosting provider that supports Node.js. For example, [AWS EC2](https://aws.amazon.com/ec2/) or a [DigitalOcean Droplet](https://www.digitalocean.com/products/droplets/). -Make sure your `package.json` has the `"build"` and `"start"` scripts: +First, ensure your `package.json` has the `"build"` and `"start"` scripts: ```json { "scripts": { - "dev": "next", + "dev": "next dev", "build": "next build", "start": "next start" } } ``` -`next build` builds the production application in the `.next` folder. After building, `next start` starts a Node.js server that supports [hybrid pages](/docs/basic-features/pages.md), serving both statically generated and server-side rendered pages. +Then, run `next build` to build your application. Finally, run `next start` to start the Node.js server. This server supports all features of Next.js. -### Docker Image +> If you are using [`next/image`](/docs/basic-features/image-optimization.md), consider adding `sharp` for more performant [Image Optimization](/docs/basic-features/image-optimization.md) in your production environment by running `npm install sharp` in your project directory. On Linux platforms, `sharp` may require [additional configuration](https://sharp.pixelplumbing.com/install#linux-memory-allocator) to prevent excessive memory usage. -<details open> - <summary><b>Examples</b></summary> - <ul> - <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-docker">with-docker</a></li> - </ul> -</details> +### Docker Image Next.js can be deployed to any hosting provider that supports [Docker](https://www.docker.com/) containers. You can use this approach when deploying to container orchestrators such as [Kubernetes](https://kubernetes.io/) or [HashiCorp Nomad](https://www.nomadproject.io/), or when running inside a single node in any cloud provider. -Here is a multi-stage `Dockerfile` using `node:alpine` that you can use: +1. [Install Docker](https://docs.docker.com/get-docker/) on your machine +1. Clone the [with-docker](https://github.com/vercel/next.js/tree/canary/examples/with-docker) example +1. Build your container: `docker build -t nextjs-docker .` +1. Run your container: `docker run -p 3000:3000 nextjs-docker` -```Dockerfile -# Install dependencies only when needed -FROM node:alpine AS deps -# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat -WORKDIR /app -COPY package.json yarn.lock ./ -RUN yarn install --frozen-lockfile - -# Rebuild the source code only when needed -FROM node:alpine AS builder -WORKDIR /app -COPY . . -COPY --from=deps /app/node_modules ./node_modules -RUN yarn build && yarn install --production --ignore-scripts --prefer-offline - -# Production image, copy all the files and run next -FROM node:alpine AS runner -WORKDIR /app - -ENV NODE_ENV production - -RUN addgroup -g 1001 -S nodejs -RUN adduser -S nextjs -u 1001 - -# You only need to copy next.config.js if you are NOT using the default configuration -# COPY --from=builder /app/next.config.js ./ -COPY --from=builder /app/public ./public -COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/package.json ./package.json +### Static HTML Export -USER nextjs +If you’d like to do a static HTML export of your Next.js app, follow the directions on our [Static HTML Export documentation](/docs/advanced-features/static-html-export.md). -EXPOSE 3000 +## Automatic Updates -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -# Uncomment the following line in case you want to disable telemetry. -# ENV NEXT_TELEMETRY_DISABLED 1 +When you deploy your Next.js application, you want to see the latest version without needing to reload. -CMD ["yarn", "start"] -``` +Next.js will automatically load the latest version of your application in the background when routing. For client-side navigations, `next/link` will temporarily function as a normal `<a>` tag. -Make sure to place this Dockerfile in the root folder of your project. +**Note:** If a new page (with an old version) has already been prefetched by `next/link`, Next.js will use the old version. Navigating to a page that has _not_ been prefetched (and is not cached at the CDN level) will load the latest version. -You can build your container with `docker build . -t my-next-js-app` and run it with `docker run -p 3000:3000 my-next-js-app`. +## Related -### Static HTML Export +For more information on what to do next, we recommend the following sections: -If you’d like to do a static HTML export of your Next.js app, follow the directions on [our documentation](/docs/advanced-features/static-html-export.md). +<div class="card"> + <a href="/docs/going-to-production.md"> + <b>Going to Production:</b> + <small>Ensure the best performance and user experience.</small> + </a> +</div> diff --git a/docs/faq.md b/docs/faq.md index 8a1cec7254a9..96025209501f 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -39,7 +39,7 @@ description: Get to know more about Next.js with the frequently asked questions. <details> <summary>How do I fetch data?</summary> - <p>It's up to you. You can use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">fetch API</a> or <a href="https://swr.vercel.app/">SWR</a> inside your React components for remote data fetching; or use our <a href="/docs/basic-features/data-fetching.md">data fetching methods</a> for initial data population.</p> + <p>It's up to you. You can use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">fetch API</a> or <a href="https://swr.vercel.app/">SWR</a> inside your React components for remote data fetching; or use our <a href="/docs/basic-features/data-fetching/overview.md">data fetching methods</a> for initial data population.</p> </details> <details> diff --git a/docs/getting-started.md b/docs/getting-started.md index ebe35fa1159a..2d78b0330dd4 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -14,7 +14,7 @@ If you have questions about anything related to Next.js, you're always welcome t #### System Requirements -- [Node.js 12.0](https://nodejs.org/) or later +- [Node.js 12.22.0](https://nodejs.org/) or later - MacOS, Windows (including WSL), and Linux are supported ## Setup @@ -22,7 +22,7 @@ If you have questions about anything related to Next.js, you're always welcome t 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 -npx create-next-app +npx create-next-app@latest # or yarn create next-app ``` @@ -30,14 +30,18 @@ yarn create next-app If you want to start with a TypeScript project you can use the `--typescript` flag: ```bash -npx create-next-app --typescript +npx create-next-app@latest --typescript # or yarn create next-app --typescript ``` -After the installation is complete, follow the instructions to start the development server. Try editing `pages/index.js` and see the result on your browser. +After the installation is complete: -For more information on how to use `create-next-app`, you can review the [`create-next-app` documentation](/docs/api-reference/create-next-app.md) +- Run `npm run dev` or `yarn dev` to start the development server on `http://localhost:3000` +- Visit `http://localhost:3000` to view your application +- Edit `pages/index.js` and see the updated result in your browser + +For more information on how to use `create-next-app`, you can review the [`create-next-app` documentation](/docs/api-reference/create-next-app.md). ## Manual Setup @@ -83,15 +87,11 @@ function HomePage() { export default HomePage ``` -To start developing your application run `npm run dev` or `yarn dev`. This starts the development server on `http://localhost:3000`. - -Visit `http://localhost:3000` to view your application. - So far, we get: - Automatic compilation and bundling (with webpack and babel) - [React Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh) -- [Static generation and server-side rendering](/docs/basic-features/data-fetching.md) of [`./pages/`](/docs/basic-features/pages.md) +- [Static generation and server-side rendering](/docs/basic-features/data-fetching/overview.md) of [`./pages/`](/docs/basic-features/pages.md) - [Static file serving](/docs/basic-features/static-file-serving.md). `./public/` is mapped to `/` In addition, any Next.js application is ready for production from the start, read more in our [Deployment documentation](/docs/deployment.md). diff --git a/docs/going-to-production.md b/docs/going-to-production.md index 55d94ef2b572..f65f055d0677 100644 --- a/docs/going-to-production.md +++ b/docs/going-to-production.md @@ -22,6 +22,7 @@ Before taking your Next.js application to production, here are some recommendati - [`next/image` and Automatic Image Optimization](/docs/basic-features/image-optimization.md) - [Automatic Font Optimization](/docs/basic-features/font-optimization.md) - [Script Optimization](/docs/basic-features/script.md) +- Improve [loading performance](#loading-performance) ## Caching @@ -38,7 +39,7 @@ Caching improves response times and reduces the number of requests to external s Cache-Control: public, max-age=31536000, immutable ``` -`Cache-Control` headers set in `next.config.js` will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](/docs/basic-features/pages.md#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) function. If you're using `next/image`, there are also [specific caching rules](/docs/basic-features/image-optimization.md#caching) for the default Image Optimization loader. +`Cache-Control` headers set in `next.config.js` will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](/docs/basic-features/pages.md#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) function. If you're using `next/image`, there are also [specific caching rules](/docs/basic-features/image-optimization.md#caching) for the default Image Optimization loader. **Note:** When running your application locally with `next dev`, your headers are overwritten to prevent caching locally. @@ -117,6 +118,26 @@ When an unhandled exception occurs, you can control the experience for your user You can also log and track exceptions with a tool like Sentry. [This example](https://github.com/vercel/next.js/tree/canary/examples/with-sentry) shows how to catch & report errors on both the client and server-side, using the Sentry SDK for Next.js. There's also a [Sentry integration for Vercel](https://vercel.com/integrations/sentry). +## Loading Performance + +To improve loading performance, you first need to determine what to measure and how to measure it. [Core Web Vitals](https://vercel.com/blog/core-web-vitals) is a good industry standard that is measured using your own web browser. If you are not familiar with the metrics of Core Web Vitals, review this [blog post](https://vercel.com/blog/core-web-vitals) and determine which specific metric/s will be your drivers for loading performance. Ideally, you would want to measure the loading performance in the following environments: + +- In the lab, using your own computer or a simulator. +- In the field, using real-world data from actual visitors. +- Local, using a test that runs on your device. +- Remote, using a test that runs in the cloud. + +Once you are able to measure the loading performance, use the following strategies to improve it iteratively so that you apply one strategy, measure the new performance and continue tweaking until you do not see much improvement. Then, you can move on to the next strategy. + +- Use caching regions that are close to the regions where your database or API is deployed. +- As described in the [caching](#caching) section, use a `stale-while-revalidate` value that will not overload your backend. +- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching#incremental-static-regeneration) to reduce the number of requests to your backend. +- Remove unused JavaScript. Review this [blog post](https://calibreapp.com/blog/bundle-size-optimization) to understand what Core Web Vitals metrics bundle size affects and what strategies you can use to reduce it, such as: + - Setting up your Code Editor to view import costs and sizes + - Finding alternative smaller packages + - Dynamically loading components and dependencies + - For more in depth information, review this [guide](https://papyrus.dev/@PapyrusBlog/how-we-reduced-next.js-page-size-by-3.5x-and-achieved-a-98-lighthouse-score) and this [performance checklist](https://dev.to/endymion1818/nextjs-performance-checklist-5gjb). + ## Related For more information on what to do next, we recommend the following sections: diff --git a/docs/guides/building-forms.md b/docs/guides/building-forms.md new file mode 100644 index 000000000000..705b73aebe8b --- /dev/null +++ b/docs/guides/building-forms.md @@ -0,0 +1,371 @@ +--- +title: Building Forms with Next.js +description: Learn how to create forms with Next.js, from the form HTML element to advanced concepts with React. +--- + +# Building Forms with Next.js + +A web form has a **client-server** relationship. They are used to send data handled by a web server for processing and storage. The form itself is the client, and the server is any storage mechanism that can be used to store, retrieve and send data when needed. + +This guide will teach you how to create a web form with Next.js. + +## Part 1: HTML Form + +HTML forms are built using the `<form>` tag. It takes a set of attributes and fields to structure the form for features like text fields, checkboxes, dropdown menus, buttons, radio buttons, etc. + +Here's the syntax of a HTML form: + +```html +<!-- Basic HTML Form --> +<form action="/send-data-here" method="post"> + <label for="first">First name:</label> + <input type="text" id="first" name="first" /> + <label for="last">Last name:</label> + <input type="text" id="last" name="last" /> + <button type="submit">Submit</button> +</form> +``` + +The front-end looks like this: + +![html forms](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/html-forms.png) + +The HTML `<form>` tag acts as a container for different `<input>` elements like `text` field and submit `button`. Let's study each of these elements: + +- `action`: An attribute that specifies where the form data is sent when the form is submitted. It's generally a URL (an absolute URL or a relative URL). +- `method`: Specifies the HTTP method, i.e., `GET` or `POST` used to send data while submitting the form. +- `<label>`: An element that defines the label for other form elements. Label aids accessibility, especially for screen readers. +- `<input>`: The form element that is widely used to structure the form fields. It depends significantly on the value of the `type` attribute. Input types can be `text`, `checkbox`, `email`, `radio`, and more. +- `<button>`: It represents a clickable button that's used to submit the form data. + +### Form Validation + +A process that checks if the information provided by a user is correct or not. Form validation also ensures that the provided information is in the correct format (e.g. there's an @ in the email field). These are of two types: + +- **Client-side**: Validation is done in the browser +- **Server-side**: Validation is done on the server + +Though both of these types are equally important, this guide will focus on client-side validation only. + +Client-side validation is further categorized as: + +- **Built-in**: Uses HTML-based attributes like `required`, `type`, `minLength`, `maxLength`, `pattern` etc. +- **JavaScript-based**: Validation that's coded with JavaScript. + +### Built-in Form Validation Using `required`, `type`, `minLength`, `maxLength` + +- `required`: Specifies which fields must be filled before submitting the form. +- `type`: Tells whether the data should be a number, email id, text string, etc. +- `minLength`: Decides the minimum length for the text data string. +- `maxLength`: Decides the maximum length for the text data string. + +The following example shows using these attributes: + +```html +<!-- HTML Form with Built-in Validation --> +<form action="/send-data-here" method="post"> + <label for="roll">Roll Number</label> + <input + type="text" + id="roll" + name="roll" + required + minlength="10" + maxlength="20" + /> + <label for="name">Name:</label> + <input type="text" id="name" name="name" required /> + <button type="submit">Submit</button> +</form> +``` + +With these validation checks in place, when a user tries to submit an empty field for Name, it gives an error that pops right in the form field. Similarly, a roll number can only be entered if it's 10-20 characters long. + +![form validation](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-validation.jpg) + +### JavaScript-based Form Validation + +Form Validation is important to ensure that a user has submitted appropriate data. JavaScript offers an additional level of validation along with HTML native form attributes on the client side. Developers generally prefer validating form data through JavaScript because its data processing is faster when compared to server-side validation. + +The following example shows using JavaScript to validate a form: + +```html +<form onsubmit="validateFormWithJS()"> + <label for="rollNumber">Roll Number:</label> + <input type="text" name="rollNumber" id="rollNumber" /> + + <label for="name">Name:</label> + <input type="text" name="name" id="name" /> + + <button type="submit">Submit</button> +</form> + +<script> + function validateFormWithJS() { + const name = document.querySelector('#name').value + const rollNumber = document.querySelector('#rollNumber').value + + if (!name) { + alert('Please enter your name.') + return false + } + + if (rollNumber.length < 3) { + alert('Roll Number should be at least 3 digits long.') + return false + } + } +</script> +``` + +The HTML [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) tag is used to embed any client-side JavaScript. It can either contain inline scripting statements (as shown in the example above) or point to an external script file via the `src` attribute. +This example validates the name and roll number of a user. The `validateFormWithJS()` function does not allow an empty name field, and the roll number must be at least three digits long. The validation is performed when you hit the Submit button. You are not redirected to the next page until the given values are correct. + +![js-validation](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/js-validation.jpg) + +#### Form Validation Using Regular Expressions + +JavaScript validation with Regular Expressions uses the `pattern` HTML attribute. A regular expression (commonly known as RegEx) is an object that describes a pattern of characters. You can only apply the `pattern` attribute to the `<input>` element. This way, you can validate the input value using Regular Expressions (RegEx) by defining your own rules. Once again, if the value does not match the defined pattern, the input will give an error. +The below example shows using the `pattern` attribute on an `input` element: + +```html +<form action="/action_page.php"> + <label for="pswrd">Password:</label> + <input + type="password" + id="pswrd" + name="pswrd" + pattern="[a-z]{0,9}" + title="Password should be digits (0 to 9) or alphabets (a to z)." + /> + + <button type="submit">Submit</button> +</form> +``` + +The password form field must only contain digits (0 to 9) and lowercase alphabets (a to z). No other characters (#,$,&, etc.) are allowed. The rule in RegEx is written as `[a-z]{1,15}`. + +![form-validate-regex](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-validate-regex.jpg) + +> To learn more about HTML forms, check out the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Learn/Forms). + +## Part 2: Project Setup + +In the following section you will be creating forms in React using Next.js. + +Create a new Next.js app. You can use the [create-next-app](https://nextjs.org/docs/getting-started#setup) for a quick start. In your command line terminal, run the following: + +``` +npx create-next-app +``` + +Answer the questions to create your project, and give it a name, this example uses [`next-forms`](https://github.com/vercel/next.js/tree/canary/examples/next-forms). Next `cd` into this directory, and run `npm run dev` or `yarn dev` command to start the development server. + +Open the URL printed in the terminal to ensure that your app is running successfully. + +## Part 3: Setting up a Next.js Form API Route + +Both the client and the server will be built using Next.js. For the server part, create an API endpoint where you will send the form data. + +Next.js offers a file-based system for routing that's built on the [concept of pages](/docs/basic-features/pages). Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a page. This [API endpoint](/docs/api-routes/introduction) is going to be server-side only. + +Go to `pages/api`, create a file called `form.js` and paste this code written in Node.js: + +```js +export default function handler(req, res) { + // Get data submitted in request's body. + const body = req.body + + // Optional logging to see the responses + // in the command line where next.js app is running. + console.log('body: ', body) + + // Both of these are required. + if (!body.first || !body.last) { + return res.json({ data: 'First or last name not found' }) + } + + // Found the name. + res.json({ data: `${body.first} ${body.last}` }) +} +``` + +This form `handler` function will receive the request `req` from the client (i.e. submitted form data). And in return, it'll send a response `res` as JSON that will have both the first and the last name. You can access this API endpoint at `http://localhost:3000/api/form` or replace the localhost URL with an actual Vercel deployment when you deploy. + +> Moreover, you can also attach this API to a database like MongoDB or Google Sheets. This way, your submitted form data will be securely stored for later use. For this guide, no database is used. Instead, the same data is returned to the user to demo how it's done. + +### Form Submission without JavaScript + +You can now use `/api/form` relative endpoint inside the `action` attribute of the form. You are sending form data to the server when the form is submitted via `POST` HTTP method (which is used to send data). + +```html +<form action="/api/form" method="post"> + <label for="first">First name:</label> + <input type="text" id="first" name="first" /> + <label for="last">Last name:</label> + <input type="text" id="last" name="last" /> + <button type="submit">Submit</button> +</form> +``` + +If you submit this form, it will submit the data to the forms API endpoint `/api/form`. The server then responds, generally handling the data and loading the URL defined by the action attribute, causing a new page load. So in this case you'll be redirected to `http://localhost:3000/api/form` with the following response from the server. + +![form-no-js](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-no-js.jpg) + +## Part 4: Configuring Forms in Next.js + +You have created a Next.js API Route for form submission. Now it's time to configure the client (the form itself) inside Next.js using React. The first step will be extending your knowledge of HTML forms and converting it into React (using [JSX](https://reactjs.org/docs/introducing-jsx.html)). + +Here's the same form in a [React function component](https://reactjs.org/docs/components-and-props.html) written using [JSX](https://reactjs.org/docs/introducing-jsx.html). + +```js +export default function Form() { + return ( + <form action="/api/form" method="post"> + <label htmlFor="first">First Name</label> + <input type="text" id="first" name="first" required /> + + <label htmlFor="last">Last Name</label> + <input type="text" id="last" name="last" required /> + + <button type="submit">Submit</button> + </form> + ) +} +``` + +Here's what changed: + +- The `for` attribute is changed to `htmlFor`. (Since `for` is a keyword associated with the "for" loop in JavaScript, React elements use `htmlFor` instead.) +- The `action` attribute now has a relative URL which is the form API endpoint. + +This completes the basic structure of your Next.js-based form. + +> You can view the entire source code of [next-forms](https://github.com/vercel/next.js/tree/canary/examples/next-forms) example repo that we're creating here as a working example. Feel free to clone it and start right away. This demo is built with create-next-app, and you can preview the basic form CSS styles inside `/styles/global.css` file. + +![forms with nextjs](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/forms-with-nextjs.png) + +## Part 5: Form Submission without JavaScript + +JavaScript brings interactivity to our web applications, but sometimes you need to control the JavaScript bundle from being too large, or your sites visitors might have JavaScript disabled. + +There are several reasons why users disable JavaScript: + +- Addressing bandwidth constraints +- Increasing device (phone or laptop) battery life +- For privacy so they won’t be tracked with analytical scripts + +Regardless of the reason, disabling JavaScript will impact site functionality partially, if not completely. + +Next open the `next-forms` directory. Inside the `/pages` directory, create a file `no-js-form.js`. + +> **Quick Tip**: In Next.js, a page is a React Component exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the pages directory. Each page is associated with a route based on its file name. +> +> Example: If you create `pages/no-js-form.js`, it will be accessible at `your-domain.tld/no-js-form`. + +Let's use the same code from above: + +```js +export default function PageWithoutJSbasedForm() { + return ( + <form action="/api/form" method="post"> + <label htmlFor="first">First Name</label> + <input type="text" id="first" name="first" required /> + + <label htmlFor="last">Last Name</label> + <input type="text" id="last" name="last" required /> + + <button type="submit">Submit</button> + </form> + ) +} +``` + +With JavaScript disabled, when you hit the Submit button, an event is triggered, which collects the form data and sends it to our forms API endpoint as defined in the `action` attribute and using `POST` HTTP `method`. You'll be redirected to the `/api/form` endpoint since that's how form `action` works. + +The form data will be submitted on the server as a request `req` to the form handler function written above. It will process the data and return a JSON string as a response `res` with your submitted name included. + +> To improve the experience here, as a response you can redirect the user to a page and thank them for submitting the form. + +## Part 6: Form Submission with JavaScript Enabled + +Inside `/pages`, you'll create another file called `js-form.js`. This will create a `/js-form` page on your Next.js app. + +Now, as soon as the form is submitted, we prevent the form's default behavior of reloading the page. We'll take the form data, convert it to JSON string, and send it to our server, the API endpoint. Finally, our server will respond with the name submitted. All of this with a basic JavaScript `handleSubmit()` function. + +Here's what this function looks like. It's well documented for you to understand each step: + +```js +export default function PageWithJSbasedForm() { + // Handles the submit event on form submit. + const handleSubmit = async (event) => { + // Stop the form from submitting and refreshing the page. + event.preventDefault() + + // Get data from the form. + const data = { + first: event.target.first.value, + last: event.target.last.value, + } + + // Send the data to the server in JSON format. + const JSONdata = JSON.stringify(data) + + // API endpoint where we send form data. + const endpoint = '/api/form' + + // Form the request for sending data to the server. + const options = { + // The method is POST because we are sending data. + method: 'POST', + // Tell the server we're sending JSON. + headers: { + 'Content-Type': 'application/json', + }, + // Body of the request is the JSON data we created above. + body: JSONdata, + } + + // Send the form data to our forms API on Vercel and get a response. + const response = await fetch(endpoint, options) + + // Get the response data from server as JSON. + // If server returns the name submitted, that means the form works. + const result = await response.json() + alert(`Is this your full name: ${result.data}`) + } + return ( + // We pass the event to the handleSubmit() function on submit. + <form onSubmit={handleSubmit}> + <label htmlFor="first">First Name</label> + <input type="text" id="first" name="first" required /> + + <label htmlFor="last">Last Name</label> + <input type="text" id="last" name="last" required /> + + <button type="submit">Submit</button> + </form> + ) +} +``` + +It's a Next.js page with a React function component called `PageWithJSbasedForm` with a `<form>` element written in JSX. There's no action on the `<form>` element. Instead, we use the `onSubmit` event handler to send data to our `{handleSubmit}` function. + +The `handleSubmit()` function processes your form data through a series of steps: + +- The `event.preventDefault()` stops the `<form>` element from refreshing the entire page. +- We created a JavaScript object called `data` with the `first` and `last` values from the form. +- JSON is a language-agnostic data transfer format. So we use `JSON.stringify(data)` to convert the data to JSON. +- We then use `fetch()` to send the data to our `/api/form` endpoint using JSON and HTTP `POST` method. +- Server sends back a response with the name submitted. Woohoo! 🥳 + +## Conclusion + +This guide has covered the following: + +- The basic HTML `form` element +- Understanding forms with React.js +- Validating forms data with and without JavaScript +- Using Next.js API Routes to handle `req` and `res` from the client and server + +For more details go through [Next.js Learn Course](https://nextjs.org/learn/basics/create-nextjs-app). diff --git a/docs/manifest.json b/docs/manifest.json index 7f0c255b01db..58e4ac62768c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -15,7 +15,38 @@ }, { "title": "Data Fetching", - "path": "/docs/basic-features/data-fetching.md" + "routes": [ + { + "path": "/docs/basic-features/data-fetching", + "redirect": { + "destination": "/docs/basic-features/data-fetching/overview" + } + }, + { + "title": "Overview", + "path": "/docs/basic-features/data-fetching/overview.md" + }, + { + "title": "getServerSideProps", + "path": "/docs/basic-features/data-fetching/get-server-side-props.md" + }, + { + "title": "getStaticPaths", + "path": "/docs/basic-features/data-fetching/get-static-paths.md" + }, + { + "title": "getStaticProps", + "path": "/docs/basic-features/data-fetching/get-static-props.md" + }, + { + "title": "Incremental Static Regeneration", + "path": "/docs/basic-features/data-fetching/incremental-static-regeneration.md" + }, + { + "title": "Client side", + "path": "/docs/basic-features/data-fetching/client-side.md" + } + ] }, { "title": "Built-in CSS Support", @@ -33,10 +64,6 @@ "title": "Font Optimization", "path": "/docs/basic-features/font-optimization.md" }, - { - "title": "Script Optimization", - "path": "/docs/basic-features/script.md" - }, { "title": "Static File Serving", "path": "/docs/basic-features/static-file-serving.md" @@ -60,6 +87,10 @@ { "title": "Supported Browsers and Features", "path": "/docs/basic-features/supported-browsers-features.md" + }, + { + "title": "Handling Scripts", + "path": "/docs/basic-features/script.md" } ] }, @@ -105,6 +136,10 @@ } ] }, + { + "title": "Middleware", + "path": "/docs/middleware.md" + }, { "title": "Going to Production", "path": "/docs/going-to-production.md" @@ -121,9 +156,22 @@ "title": "Testing", "path": "/docs/testing.md" }, + { + "title": "Guides", + "routes": [ + { + "title": "Building Forms", + "path": "/docs/guides/building-forms.md" + } + ] + }, { "title": "Advanced Features", "routes": [ + { + "title": "Next.js Compiler", + "path": "/docs/advanced-features/compiler.md" + }, { "title": "Preview Mode", "path": "/docs/advanced-features/preview-mode.md" @@ -144,6 +192,10 @@ "title": "Absolute Imports and Module Path Aliases", "path": "/docs/advanced-features/module-path-aliases.md" }, + { + "title": "Using MDX", + "path": "/docs/advanced-features/using-mdx.md" + }, { "title": "AMP Support", "routes": [ @@ -221,9 +273,17 @@ "title": "Internationalized Routing", "path": "/docs/advanced-features/i18n-routing.md" }, + { + "title": "Output File Tracing", + "path": "/docs/advanced-features/output-file-tracing.md" + }, { "title": "Security Headers", "path": "/docs/advanced-features/security-headers.md" + }, + { + "title": "React 18", + "path": "/docs/advanced-features/react-18.md" } ] }, @@ -276,6 +336,10 @@ "title": "next/image", "path": "/docs/api-reference/next/image.md" }, + { + "title": "next/script", + "path": "/docs/api-reference/next/script.md" + }, { "title": "next/head", "path": "/docs/api-reference/next/head.md" @@ -284,12 +348,32 @@ "title": "next/amp", "path": "/docs/api-reference/next/amp.md" }, + { + "title": "next/server", + "path": "/docs/api-reference/next/server.md" + }, + { + "title": "Edge Runtime", + "path": "/docs/api-reference/edge-runtime.md" + }, { "title": "Data Fetching", "routes": [ { "title": "getInitialProps", - "path": "/docs/api-reference/data-fetching/getInitialProps.md" + "path": "/docs/api-reference/data-fetching/get-initial-props.md" + }, + { + "title": "getServerSideProps", + "path": "/docs/api-reference/data-fetching/get-server-side-props.md" + }, + { + "title": "getStaticPaths", + "path": "/docs/api-reference/data-fetching/get-static-paths.md" + }, + { + "title": "getStaticProps", + "path": "/docs/api-reference/data-fetching/get-static-props.md" } ] }, @@ -387,6 +471,14 @@ { "title": "React Strict Mode", "path": "/docs/api-reference/next.config.js/react-strict-mode.md" + }, + { + "title": "URL Imports", + "path": "/docs/api-reference/next.config.js/url-imports.md" + }, + { + "title": "Build indicator", + "path": "/docs/api-reference/next.config.js/build-indicator.md" } ] } diff --git a/docs/middleware.md b/docs/middleware.md new file mode 100644 index 000000000000..2e717c262241 --- /dev/null +++ b/docs/middleware.md @@ -0,0 +1,115 @@ +--- +description: Learn how to use Middleware in Next.js to run code before a request is completed. +--- + +# Middleware + +Middleware enables you to use code over configuration. This gives you full flexibility in Next.js, because you can run code before a request is completed. Based on the user's incoming request, you can modify the response by rewriting, redirecting, adding headers, or even streaming HTML. + +## Usage + +1. Install the latest version of Next.js: + +```jsx +npm install next@latest +``` + +2. Then, create a `_middleware.ts` file under your `/pages` directory. + +3. Finally, export a middleware function from the `_middleware.ts` file. + +```jsx +// pages/_middleware.ts + +import type { NextFetchEvent, NextRequest } from 'next/server' + +export function middleware(req: NextRequest, ev: NextFetchEvent) { + return new Response('Hello, world!') +} +``` + +In this example, we use the standard Web API Response ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Response)). + +## API + +Middleware is created by using a `middleware` function that lives inside a `_middleware` file. Its API is based upon the native [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), and [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) objects. + +These native Web API objects are extended to give you more control over how you manipulate and configure a response, based on the incoming requests. + +The function signature: + +```ts +import type { NextFetchEvent } from 'next/server' +import type { NextRequest } from 'next/server' + +export type Middleware = ( + request: NextRequest, + event: NextFetchEvent +) => Promise<Response | undefined> | Response | undefined +``` + +The function can be a default export and as such, does **not** have to be named `middleware`. Though this is a convention. Also note that you only need to make the function `async` if you are running asynchronous code. + +**Note:** Edge Functions are currently in Beta. The API might change as we look to continually make improvements. + +## Examples + +Middleware can be used for anything that shares logic for a set of pages, including: + +- [Authentication](https://github.com/vercel/examples/tree/main/edge-functions) +- [Bot protection](https://github.com/vercel/examples/tree/main/edge-functions) +- [Redirects and rewrites](https://github.com/vercel/examples/tree/main/edge-functions) +- [Handling unsupported browsers](https://github.com/vercel/examples/tree/main/edge-functions) +- [Feature flags and A/B tests](https://github.com/vercel/examples/tree/main/edge-functions) +- [Server-side analytics](https://github.com/vercel/examples/tree/main/edge-functions) +- [Advanced i18n routing requirements](https://github.com/vercel/examples/tree/main/edge-functions) +- [Logging](https://github.com/vercel/examples/tree/main/edge-functions) + +## Execution Order + +If your Middleware is created in `/pages/_middleware.ts`, it will run on all routes within the `/pages` directory. The below example assumes you have `about.tsx` and `teams.tsx` routes. + +```bash +- package.json +- /pages + _middleware.ts # Will run on all routes under /pages + index.tsx + about.tsx + teams.tsx +``` + +If you _do_ have sub-directories with nested routes, Middleware will run from the top down. For example, if you have `/pages/about/_middleware.ts` and `/pages/about/team/_middleware.ts`, `/about` will run first and then `/about/team`. The below example shows how this works with a nested routing structure. + +```bash +- package.json +- /pages + index.tsx + - /about + _middleware.ts # Will run first + about.tsx + - /teams + _middleware.ts # Will run second + teams.tsx +``` + +Middleware runs directly after `redirects` and `headers`, before the first filesystem lookup. This excludes `/_next` files. + +## Deployment + +Middleware uses a [strict runtime](/docs/api-reference/edge-runtime.md) that supports standard Web APIs like `fetch`. This works out of the box using `next start`, as well as on Edge platforms like Vercel, which use [Edge Functions](http://www.vercel.com/edge). + +## Related + +<div class="card"> + <a href="/docs/api-reference/next/server.md"> + <b>Middleware API Reference</b> + <small>Learn more about the supported APIs for Middleware.</small> + </a> +</div> + +<div class="card"> + <a href="/docs/api-reference/edge-runtime.md"> + <b>Edge Runtime</b> + <small>Learn more about the supported Web APIs available.</small> + </a> +</div> diff --git a/docs/migrating/from-create-react-app.md b/docs/migrating/from-create-react-app.md index 07ff81fd30cd..54bf69da832a 100644 --- a/docs/migrating/from-create-react-app.md +++ b/docs/migrating/from-create-react-app.md @@ -6,8 +6,8 @@ description: Learn how to transition an existing Create React App project to Nex This guide will help you understand how to transition from an existing non-ejected Create React App project to Next.js. Migrating to Next.js will allow you to: -- Choose which [data fetching](/docs/basic-features/data-fetching.md) strategy you want on a per-page basis. -- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching.md#incremental-static-regeneration) to update _existing_ pages by re-rendering them in the background as traffic comes in. +- Choose which [data fetching](/docs/basic-features/data-fetching/overview.md) strategy you want on a per-page basis. +- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) to update _existing_ pages by re-rendering them in the background as traffic comes in. - Use [API Routes](/docs/api-routes/introduction.md). And more! Let’s walk through a series of steps to complete the migration. @@ -51,7 +51,7 @@ Create React App uses the `public` directory for the [entry HTML file](https://c With Create React App, you're likely using React Router. Instead of using a third-party library, Next.js includes its own [file-system based routing](/docs/routing/introduction.md). - Convert all `Route` components to new files in the `pages` directory. -- For routes that require dynamic content (e.g. `/blog/:slug`), you can use [Dynamic Routes](/docs/routing/dynamic-routes.md) with Next.js (e.g. `pages/blog/[slug].js`). The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation)). +- For routes that require dynamic content (e.g. `/blog/:slug`), you can use [Dynamic Routes](/docs/routing/dynamic-routes.md) with Next.js (e.g. `pages/blog/[slug].js`). The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching/get-static-paths.md)). For more information, see [Migrating from React Router](/docs/migrating/from-react-router.md). @@ -114,7 +114,7 @@ export default function Home() { ## Environment Variables -Next.js has support for `.env` [Environment Variables](/docs/basic-features/environment-variables.md) similar to Create React App. The main different is the prefix used to expose environment variables on the client-side. +Next.js has support for `.env` [Environment Variables](/docs/basic-features/environment-variables.md) similar to Create React App. The main difference is the prefix used to expose environment variables on the client-side. - Change all environment variables with the `REACT_APP_` prefix to `NEXT_PUBLIC_`. - Server-side environment variables will be available at build-time and in [API Routes](/docs/api-routes/introduction.md). diff --git a/docs/migrating/from-gatsby.md b/docs/migrating/from-gatsby.md index 4601a7850ad8..36d27b38a8b1 100644 --- a/docs/migrating/from-gatsby.md +++ b/docs/migrating/from-gatsby.md @@ -6,8 +6,8 @@ description: Learn how to transition an existing Gatsby project to Next.js. This guide will help you understand how to transition from an existing Gatsby project to Next.js. Migrating to Next.js will allow you to: -- Choose which [data fetching](/docs/basic-features/data-fetching.md) strategy you want on a per-page basis. -- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching.md#incremental-static-regeneration) to update _existing_ pages by re-rendering them in the background as traffic comes in. +- Choose which [data fetching](/docs/basic-features/data-fetching/overview.md) strategy you want on a per-page basis. +- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) to update _existing_ pages by re-rendering them in the background as traffic comes in. - Use [API Routes](/docs/api-routes/introduction.md). And more! Let’s walk through a series of steps to complete the migration. @@ -52,7 +52,7 @@ Both Gatsby and Next support a `pages` directory, which uses [file-system based Gatsby creates dynamic routes using the `createPages` API inside of `gatsby-node.js`. With Next, we can use [Dynamic Routes](/docs/routing/dynamic-routes.md) inside of `pages` to achieve the same effect. Rather than having a `template` directory, you can use the React component inside your dynamic route file. For example: - **Gatsby:** `createPages` API inside `gatsby-node.js` for each blog post, then have a template file at `src/templates/blog-post.js`. -- **Next:** Create `pages/blog/[slug].js` which contains the blog post template. The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation)). +- **Next:** Create `pages/blog/[slug].js` which contains the blog post template. The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching/get-static-paths.md)). ## Styling @@ -92,13 +92,13 @@ Update any import statements, switch `to` to `href`, and add an `<a>` tag as a c The largest difference between Gatsby and Next.js is how data fetching is implemented. Gatsby is opinionated with GraphQL being the default strategy for retrieving data across your application. With Next.js, you get to choose which strategy you want (GraphQL is one supported option). -Gatsby uses the `graphql` tag to query data in the pages of your site. This may include local data, remote data, or information about your site configuration. Gatsby only allows the creation of static pages. With Next.js, you can choose on a [per-page basis](/docs/basic-features/pages.md) which [data fetching strategy](/docs/basic-features/data-fetching.md) you want. For example, `getServerSideProps` allows you to do server-side rendering. If you wanted to generate a static page, you'd export `getStaticProps` / `getStaticPaths` inside the page, rather than using `pageQuery`. For example: +Gatsby uses the `graphql` tag to query data in the pages of your site. This may include local data, remote data, or information about your site configuration. Gatsby only allows the creation of static pages. With Next.js, you can choose on a [per-page basis](/docs/basic-features/pages.md) which [data fetching strategy](/docs/basic-features/data-fetching/overview.md) you want. For example, `getServerSideProps` allows you to do server-side rendering. If you wanted to generate a static page, you'd export `getStaticProps` / `getStaticPaths` inside the page, rather than using `pageQuery`. For example: ```js // src/pages/[slug].js // Install remark and remark-html -import remark from 'remark' +import { remark } from 'remark' import html from 'remark-html' import { getPostBySlug, getAllPosts } from '../lib/blog' @@ -167,7 +167,7 @@ export function getAllPosts() { ## Image Component and Image Optimization -Since version **10.0.0**, Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). +Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web. @@ -185,17 +185,28 @@ This means you can remove common Gatsby plugins like: Instead, use the built-in [`next/image`](/docs/api-reference/next/image.md) component and [Automatic Image Optimization](/docs/basic-features/image-optimization.md). +> The `next/image` component's default loader is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). However, other loader options will work. + ```jsx import Image from 'next/image' +import profilePic from '../public/me.png' export default function Home() { return ( <> <h1>My Homepage</h1> <Image - src="/me.png" + src={profilePic} alt="Picture of the author" + // When "responsive", similar to "fluid" from Gatsby + // When "intrinsic", similar to "fluid" with maxWidth from Gatsby + // When "fixed", similar to "fixed" from Gatsby + layout="responsive" + // Optional, similar to "blur-up" from Gatsby + placeholder="blur" + // Optional, similar to "width" in Gatsby GraphQL width={500} + // Optional, similar to "height" in Gatsby GraphQL height={500} /> <p>Welcome to my homepage!</p> diff --git a/docs/routing/imperatively.md b/docs/routing/imperatively.md index 7dbbf7a16a42..f3c4e589bb83 100644 --- a/docs/routing/imperatively.md +++ b/docs/routing/imperatively.md @@ -18,13 +18,13 @@ The following example shows how to do basic page navigations with [`useRouter`]( ```jsx import { useRouter } from 'next/router' -function ReadMore() { +export default function ReadMore() { const router = useRouter() return ( - <span onClick={() => router.push('/about')}>Click here to read more</span> + <button onClick={() => router.push('/about')}> + Click here to read more + </button> ) } - -export default ReadMore ``` diff --git a/docs/routing/introduction.md b/docs/routing/introduction.md index 575c65db1792..c2b8c8ddc8cd 100644 --- a/docs/routing/introduction.md +++ b/docs/routing/introduction.md @@ -6,7 +6,7 @@ description: Next.js has a built-in, opinionated, and file-system based Router. Next.js has a file-system based router built on the [concept of pages](/docs/basic-features/pages.md). -When a file is added to the `pages` directory it's automatically available as a route. +When a file is added to the `pages` directory, it's automatically available as a route. The files inside the `pages` directory can be used to define most common patterns. @@ -19,14 +19,14 @@ The router will automatically route files named `index` to the root of the direc #### Nested routes -The router supports nested files. If you create a nested folder structure files will be automatically routed in the same way still. +The router supports nested files. If you create a nested folder structure, files will automatically be routed in the same way still. - `pages/blog/first-post.js` → `/blog/first-post` - `pages/dashboard/settings/username.js` → `/dashboard/settings/username` #### Dynamic route segments -To match a dynamic segment you can use the bracket syntax. This allows you to match named parameters. +To match a dynamic segment, you can use the bracket syntax. This allows you to match named parameters. - `pages/blog/[slug].js` → `/blog/:slug` (`/blog/hello-world`) - `pages/[username]/settings.js` → `/:username/settings` (`/foo/settings`) @@ -68,13 +68,13 @@ function Home() { export default Home ``` -In the example above we have multiple links, each one maps a path (`href`) to a known page: +The example above uses multiple links. Each one maps a path (`href`) to a known page: - `/` → `pages/index.js` - `/about` → `pages/about.js` - `/blog/hello-world` → `pages/blog/[slug].js` -Any `<Link />` in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using [Static Generation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). The corresponding data for [server-rendered](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) routes is _not_ prefetched. +Any `<Link />` in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using [Static Generation](/docs/basic-features/data-fetching/get-static-props.md). The corresponding data for [server-rendered](/docs/basic-features/data-fetching/get-server-side-props.md) routes is _not_ prefetched. ### Linking to dynamic paths diff --git a/docs/routing/shallow-routing.md b/docs/routing/shallow-routing.md index caa9cf35138e..d1703af6b6e1 100644 --- a/docs/routing/shallow-routing.md +++ b/docs/routing/shallow-routing.md @@ -11,7 +11,7 @@ description: You can use shallow routing to change the URL without triggering a </ul> </details> -Shallow routing allows you to change the URL without running data fetching methods again, that includes [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering), [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation), and [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md). +Shallow routing allows you to change the URL without running data fetching methods again, that includes [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md), [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md), and [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md). You'll receive the updated `pathname` and the `query` via the [`router` object](/docs/api-reference/next/router.md#router-object) (added by [`useRouter`](/docs/api-reference/next/router.md#useRouter) or [`withRouter`](/docs/api-reference/next/router.md#withRouter)), without losing state. diff --git a/docs/testing.md b/docs/testing.md index 6d723e340774..6b0bc97e2699 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -1,5 +1,5 @@ --- -description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Jest, and React Testing Library. +description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Jest, and React Testing Library. --- # Testing @@ -8,11 +8,12 @@ description: Learn how to set up Next.js with three commonly used testing tools <summary><b>Examples</b></summary> <ul> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-cypress">Next.js with Cypress</a></li> + <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-playwright">Next.js with Playwright</a></li> <li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-jest">Next.js with Jest and React Testing Library</a></li> </ul> </details> -Learn how to set up Next.js with three commonly used testing tools: [Cypress](https://www.cypress.io/blog/2021/04/06/cypress-component-testing-react/), [Jest](https://jestjs.io/docs/tutorial-react), and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). +Learn how to set up Next.js with commonly used testing tools: [Cypress](https://nextjs.org/docs/testing#cypress), [Playwright](https://nextjs.org/docs/testing#playwright), and [Jest with React Testing Library](https://nextjs.org/docs/testing#jest-and-react-testing-library). ## Cypress @@ -23,7 +24,7 @@ Cypress is a test runner used for **End-to-End (E2E)** and **Integration Testing You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started. ```bash -npx create-next-app --example with-cypress with-cypress-app +npx create-next-app@latest --example with-cypress with-cypress-app ``` ### Manual setup @@ -113,7 +114,7 @@ Since Cypress is testing a real Next.js application, it requires the Next.js ser Run `npm run build` and `npm run start`, then run `npm run cypress` in another terminal window to start Cypress. -> **Note:** Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test start http://localhost:3000 cypress"` to start the Next.js production server in conjuction with Cypress. Remember to rebuild your application after new changes. +> **Note:** Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test start http://localhost:3000 cypress"` to start the Next.js production server in conjunction with Cypress. Remember to rebuild your application after new changes. ### Getting ready for Continuous Integration (CI) @@ -135,35 +136,179 @@ You can learn more about Cypress and Continuous Integration from these resources - [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) - [Cypress GitHub Actions Guide](https://on.cypress.io/github-actions) -- [Official Cypress Github Action](https://github.com/cypress-io/github-action) +- [Official Cypress GitHub Action](https://github.com/cypress-io/github-action) -## Jest and React Testing Library +## Playwright -Jest and React Testing Library are frequently used together for **Unit Testing**. +Playwright is a testing framework that lets you automate Chromium, Firefox, and WebKit with a single API. You can use it to write **End-to-End (E2E)** and **Integration** tests across all platforms. ### Quickstart -You can use `create-next-app` with the [with-jest example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) to quickly get started with Jest and React Testing Library: +The fastest way to get started, is to use `create-next-app` with the [with-playwright example](https://github.com/vercel/next.js/tree/canary/examples/with-playwright). This will create a Next.js project complete with Playwright all set up. ```bash -npx create-next-app --example with-jest with-jest-app +npx create-next-app@latest --example with-playwright with-playwright-app ``` ### Manual setup -To manually set up Jest and React Testing Library, install `jest` , `@testing-library/react`, `@testing-library/jest-dom` as well as some supporting packages: +You can also use `npm init playwright` to add Playwright to an existing `NPM` project. + +To manually get started with Playwright, install the `@playwright/test` package: + +```bash +npm install --save-dev @playwright/test +``` + +Add Playwright to the `package.json` scripts field: + +```json +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test:e2e": "playwright test", +} +``` + +### Creating your first Playwright end-to-end test + +Assuming the following two Next.js pages: + +```jsx +// pages/index.js +import Link from 'next/link' + +export default function Home() { + return ( + <nav> + <Link href="/about"> + <a>About</a> + </Link> + </nav> + ) +} +``` + +```jsx +// pages/about.js +export default function About() { + return ( + <div> + <h1>About Page</h1> + </div> + ) +} +``` + +Add a test to verify that your navigation is working correctly: + +```jsx +// e2e/example.spec.ts + +import { test, expect } from '@playwright/test' + +test('should navigate to the about page', async ({ page }) => { + // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) + await page.goto('http://localhost:3000/') + // Find an element with the text 'About Page' and click on it + await page.click('text=About Page') + // The new url should be "/about" (baseURL is used there) + await expect(page).toHaveURL('http://localhost:3000/about') + // The new page should contain an h1 with "About Page" + await expect(page.locator('h1')).toContainText('About Page') +}) +``` + +You can use `page.goto("/")` instead of `page.goto("http://localhost:3000/")`, if you add [`"baseURL": "http://localhost:3000"`](https://playwright.dev/docs/api/class-testoptions#test-options-base-url) to the `playwright.config.ts` configuration file. + +### Running your Playwright tests + +Since Playwright is testing a real Next.js application, it requires the Next.js server to be running prior to starting Playwright. It is recommended to run your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build` and `npm run start`, then run `npm run test:e2e` in another terminal window to run the Playwright tests. + +> **Note:** Alternatively, you can use the [`webServer`](https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests) feature to let Playwright start the development server and wait until it's fully available. + +### Running Playwright on Continuous Integration (CI) + +Playwright will by default run your tests in the [headed mode](https://playwright.dev/docs/ci). To install all the Playwright dependencies, run `npx playwright install-deps`. + +You can learn more about Playwright and Continuous Integration from these resources: + +- [Getting started with Playwright](https://playwright.dev/docs/intro) +- [Use a development server](https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests) +- [Playwright on your CI provider](https://playwright.dev/docs/ci) + +## Jest and React Testing Library + +Jest and React Testing Library are frequently used together for **Unit Testing**. There are three ways you can start using Jest within your Next.js application: + +1. Using one of our [quickstart examples](https://nextjs.org/docs/testing#quickstart-2) +2. With the [Next.js Rust Compiler](https://nextjs.org/docs/testing#setting-up-jest-with-the-rust-compiler) +3. With [Babel](https://nextjs.org/docs/testing#setting-up-jest-with-babel) + +The following sections will go through how you can set up Jest with each of these options: + +### Quickstart + +You can use `create-next-app` with the [with-jest](https://github.com/vercel/next.js/tree/canary/examples/with-jest) example to quickly get started with Jest and React Testing Library: ```bash -npm install --save-dev jest babel-jest @testing-library/react @testing-library/jest-dom identity-obj-proxy react-test-renderer +npx create-next-app@latest --example with-jest with-jest-app ``` -**Configuring Jest** +### Setting up Jest (with the Rust Compiler) + +Since the release of [Next.js 12](https://nextjs.org/blog/next-12), Next.js now has built-in configuration for Jest. + +To set up Jest, install `jest` , `@testing-library/react`, `@testing-library/jest-dom`: + +```bash +npm install --save-dev jest @testing-library/react @testing-library/jest-dom +``` -Create a `jest.config.js` file in your project's root directory and add the following configuration options: +Create a `jest.config.js` file in your project's root directory and add the following: ```jsx // jest.config.js +const nextJest = require('next/jest') + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', +}) + +// Add any custom config to be passed to Jest +const customJestConfig = { + // Add more setup options before each test is run + // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], + // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work + moduleDirectories: ['node_modules', '<rootDir>/'], + testEnvironment: 'jest-environment-jsdom', +} +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +module.exports = createJestConfig(customJestConfig) +``` + +Under the hood, `next/jest` is automatically configuring Jest for you, including: + +- Setting up `transform` using [SWC](https://nextjs.org/docs/advanced-features/compiler) +- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants) and image imports +- Loading `.env` (and all variants) into `process.env` +- Ignoring `node_modules` from test resolving and transforms +- Ignoring `.next` from test resolving +- Loading `next.config.js` for flags that enable SWC transforms + +### Setting up Jest (with Babel) + +If you opt-out of the [Rust Compiler](https://nextjs.org/docs/advanced-features/compiler), you will need to manually configure Jest and install `babel-jest` and `identity-obj-proxy` in addition to the packages above. + +Here are the recommended options to configure Jest for Next.js: + +```jsx +// jest.config.js module.exports = { collectCoverageFrom: [ '**/*.{js,jsx,ts,tsx}', @@ -171,22 +316,27 @@ module.exports = { '!**/node_modules/**', ], moduleNameMapper: { - /* Handle CSS imports (with CSS modules) - https://jestjs.io/docs/webpack#mocking-css-modules */ + // Handle CSS imports (with CSS modules) + // https://jestjs.io/docs/webpack#mocking-css-modules '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', // Handle CSS imports (without CSS modules) '^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js', - /* Handle image imports - https://jestjs.io/docs/webpack#handling-static-assets */ - '^.+\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js', + // Handle image imports + // https://jestjs.io/docs/webpack#handling-static-assets + '^.+\\.(jpg|jpeg|png|gif|webp|avif|svg)$': `<rootDir>/__mocks__/fileMock.js`, + + // Handle module aliases + '^@/components/(.*)$': '<rootDir>/components/$1', }, + // Add more setup options before each test is run + // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'], testEnvironment: 'jsdom', transform: { - /* Use babel-jest to transpile tests with the next/babel preset - https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object */ + // Use babel-jest to transpile tests with the next/babel preset + // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], }, transformIgnorePatterns: [ @@ -196,33 +346,37 @@ module.exports = { } ``` -You can learn more about each option above in the [Jest docs](https://jestjs.io/docs/configuration). +You can learn more about each configuration option in the [Jest docs](https://jestjs.io/docs/configuration). **Handling stylesheets and image imports** -These files aren't useful in tests but importing them may cause errors, so we will need to mock them. Create the mock files we referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: +Stylesheets and images aren't used in the tests but importing them may cause errors, so they will need to be mocked. Create the mock files referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: -```json +```js // __mocks__/fileMock.js - -(module.exports = "test-file-stub") +module.exports = 'test-file-stub' ``` -```json +```js // __mocks__/styleMock.js +module.exports = {} +``` + +If you're running into the issue `"Failed to parse src "test-file-stub" on 'next/image'"`, add a '/' to your fileMock. -module.exports = {}; +```js +// __mocks__/fileMock.js +module.exports = '/test-file-stub' ``` For more information on handling static assets, please refer to the [Jest Docs](https://jestjs.io/docs/webpack#handling-static-assets). -**Extend Jest with custom matchers** +**Optional: Extend Jest with custom matchers** `@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: -```json +```js // jest.config.js - setupFilesAfterEnv: ['<rootDir>/jest.setup.js'] ``` @@ -230,13 +384,12 @@ Then, inside `jest.setup.js`, add the following import: ```jsx // jest.setup.js - import '@testing-library/jest-dom/extend-expect' ``` If you need to add more setup options before each test, it's common to add them to the `jest.setup.js` file above. -**Absolute Imports and Module Path Aliases** +**Optional: Absolute Imports and Module Path Aliases** If your project is using [Module Path Aliases](https://nextjs.org/docs/advanced-features/module-path-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: @@ -259,6 +412,8 @@ moduleNameMapper: { } ``` +### Creating your tests: + **Add a test script to package.json** Add the Jest executable in watch mode to the `package.json` scripts: @@ -278,16 +433,11 @@ Add the Jest executable in watch mode to the `package.json` scripts: Your project is now ready to run tests. Follow Jests convention by adding tests to the `__tests__` folder in your project's root directory. -For example, we can add a test to check if the `<Index />` component successfully renders a heading: +For example, we can add a test to check if the `<Home />` component successfully renders a heading: ```jsx // __tests__/index.test.jsx -/** - * @jest-environment jsdom - */ - -import React from 'react' import { render, screen } from '@testing-library/react' import Home from '../pages/index' @@ -304,19 +454,17 @@ describe('Home', () => { }) ``` -> **Note**: The `@jest-environment jsdom` comment above configures the testing environment as `jsdom` inside the test file because React Testing Library uses DOM elements like `document.body` which will not work in Jest's default `node` testing environment. Alternatively, you can also set the `jsdom` environment globally by adding the Jest configuration option: `"testEnvironment": "jsdom"` in `jest.config.js`. - -Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `<Index />` component: +Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `<Home />` component: ```jsx // __tests__/snapshot.js -import React from 'react' -import renderer from 'react-test-renderer' -import Index from '../pages/index' + +import { render } from '@testing-library/react' +import Home from '../pages/index' it('renders homepage unchanged', () => { - const tree = renderer.create(<Index />).toJSON() - expect(tree).toMatchSnapshot() + const { container } = render(<Home />) + expect(container).toMatchSnapshot() }) ``` @@ -324,7 +472,7 @@ it('renders homepage unchanged', () => { **Running your test suite** -Run `npm run jest` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. +Run `npm run test` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. For further reading, you may find these resources helpful: @@ -343,7 +491,7 @@ The Next.js community has created packages and articles you may find helpful: For more information on what to read next, we recommend: <div class="card"> - <a href="/docs/basic-features/environment-variables#test-environment-variable.md"> + <a href="/docs/basic-features/environment-variables#test-environment-variables.md"> <b>Test Environment Variables</b> <small>Learn more about the test environment variables.</small> </a> diff --git a/docs/upgrading.md b/docs/upgrading.md index 08a607ae83f4..f53b2b6930b0 100644 --- a/docs/upgrading.md +++ b/docs/upgrading.md @@ -4,6 +4,127 @@ description: Learn how to upgrade Next.js. # Upgrade Guide +## Upgrading from 11 to 12 + +### Minimum Node.js version + +The minimum Node.js version has been bumped from 12.0.0 to 12.22.0 which is the first version of Node.js with native ES Modules support. + +### Upgrade React version to latest + +To upgrade you can run the following command: + +``` +npm install react@latest react-dom@latest +``` + +Or using `yarn`: + +``` +yarn add react@latest react-dom@latest +``` + +### Upgrade Next.js version to 12 + +To upgrade you can run the following command in the terminal: + +``` +npm install next@12 +``` + +or + +``` +yarn add next@12 +``` + +### SWC replacing Babel + +Next.js now uses Rust-based compiler [SWC](https://swc.rs/) to compile JavaScript/TypeScript. This new compiler is up to 17x faster than Babel when compiling individual files and up to 5x faster Fast Refresh. + +Next.js provides full backwards compatibility with applications that have [custom Babel configuration](https://nextjs.org/docs/advanced-features/customizing-babel-config). All transformations that Next.js handles by default like styled-jsx and tree-shaking of `getStaticProps` / `getStaticPaths` / `getServerSideProps` have been ported to Rust. + +When an application has a custom Babel configuration, Next.js will automatically opt-out of using SWC for compiling JavaScript/Typescript and will fall back to using Babel in the same way that it was used in Next.js 11. + +Many of the integrations with external libraries that currently require custom Babel transformations will be ported to Rust-based SWC transforms in the near future. These include but are not limited to: + +- Styled Components +- Emotion +- Relay + +In order to prioritize transforms that will help you adopt SWC, please provide your `.babelrc` on [the feedback thread](https://github.com/vercel/next.js/discussions/30174). + +### SWC replacing Terser for minification + +You can opt-in to replacing Terser with SWC for minifying JavaScript up to 7x faster using a flag in `next.config.js`: + +```js +module.exports = { + swcMinify: true, +} +``` + +Minification using SWC is an opt-in flag to ensure it can be tested against more real-world Next.js applications before it becomes the default in Next.js 12.1. If you have feedback about minification, please leave it on [the feedback thread](https://github.com/vercel/next.js/discussions/30237). + +### Improvements to styled-jsx CSS parsing + +On top of the Rust-based compiler we've implemented a new CSS parser based on the CSS parser that was used for the styled-jsx Babel transform. This new parser has improved handling of CSS and now errors when invalid CSS is used that would previously slip through and cause unexpected behavior. + +Because of this change invalid CSS will throw an error during development and `next build`. This change only affects styled-jsx usage. + +### `next/image` changed wrapping element + +`next/image` now renders the `<img>` inside a `<span>` instead of `<div>`. + +If your application has specific CSS targeting span, for example `.container span`, upgrading to Next.js 12 might incorrectly match the wrapping element inside the `<Image>` component. You can avoid this by restricting the selector to a specific class such as `.container span.item` and updating the relevant component with that className, such as `<span className="item" />`. + +If your application has specific CSS targeting the `next/image` `<div>` tag, for example `.container div`, it may not match anymore. You can update the selector `.container span`, or preferably, add a new `<div className="wrapper">` wrapping the `<Image>` component and target that instead such as `.container .wrapper`. + +The `className` prop is unchanged and will still be passed to the underlying `<img>` element. + +See the [documentation](https://nextjs.org/docs/basic-features/image-optimization#styling) for more info. + +### Next.js' HMR connection now uses a WebSocket + +Previously, Next.js used a [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) connection to receive HMR events. Next.js 12 now uses a WebSocket connection. + +In some cases when proxying requests to the Next.js dev server, you will need to ensure the upgrade request is handled correctly. For example, in `nginx` you would need to add the following configuration: + +```nginx +location /_next/webpack-hmr { + proxy_pass http://localhost:3000/_next/webpack-hmr; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; +} +``` + +For custom servers, such as `express`, you may need to use `app.all` to ensure the request is passed correctly, for example: + +```js +app.all('/_next/webpack-hmr', (req, res) => { + nextjsRequestHandler(req, res) +}) +``` + +### Webpack 4 support has been removed + +If you are already using webpack 5 you can skip this section. + +Next.js has adopted webpack 5 as the default for compilation in Next.js 11. As communicated in the [webpack 5 upgrading documentation](https://nextjs.org/docs/messages/webpack5) Next.js 12 removes support for webpack 4. + +If your application is still using webpack 4 using the opt-out flag you will now see an error linking to the [webpack 5 upgrading documentation](https://nextjs.org/docs/messages/webpack5). + +### `target` option deprecated + +If you do not have `target` in `next.config.js` you can skip this section. + +The target option has been deprecated in favor of built-in support for tracing what dependencies are needed to run a page. + +During `next build`, Next.js will automatically trace each page and its dependencies to determine all of the files that are needed for deploying a production version of your application. + +If you are currently using the `target` option set to `serverless` please read the [documentation on how to leverage the new output](https://nextjs.org/docs/advanced-features/output-file-tracing). + ## Upgrading from version 10 to 11 ### Upgrade React version to latest @@ -22,18 +143,18 @@ Or using `yarn`: yarn add react@latest react-dom@latest ``` -### Upgrade Next.js version to latest +### Upgrade Next.js version to 11 To upgrade you can run the following command in the terminal: ``` -npm install next@latest +npm install next@11 ``` or ``` -yarn add next@latest +yarn add next@11 ``` ### Webpack 5 @@ -96,7 +217,7 @@ The `modules` and `render` option for `next/dynamic` have been deprecated since This option hasn't been mentioned in the documentation since Next.js 8 so it's less likely that your application is using it. -If you application does use `modules` and `render` you can refer to [the documentation](https://nextjs.org/docs/messages/next-dynamic-modules). +If your application does use `modules` and `render` you can refer to [the documentation](https://nextjs.org/docs/messages/next-dynamic-modules). ### Remove `Head.rewind` diff --git a/errors/beta-middleware.md b/errors/beta-middleware.md new file mode 100644 index 000000000000..fed56e1573c9 --- /dev/null +++ b/errors/beta-middleware.md @@ -0,0 +1,13 @@ +# Beta Middleware Used + +#### Why This Error Occurred + +The middleware feature of Next.js is still in beta so this warning is shown to express that the feature is not yet covered by semver and can experience changes at any point. + +#### Possible Ways to Fix It + +No fix necessary. + +### Useful Links + +- [semver documentation](https://semver.org/) diff --git a/errors/conflicting-ssg-paths.md b/errors/conflicting-ssg-paths.md index eb3e71a965bd..d7f03e9a8274 100644 --- a/errors/conflicting-ssg-paths.md +++ b/errors/conflicting-ssg-paths.md @@ -65,4 +65,4 @@ export default function CatchAll() { ### Useful Links -- [`getStaticPaths` Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation) +- [`getStaticPaths` Documentation](https://nextjs.org/docs/basic-features/data-fetching/get-static-paths.md) diff --git a/errors/custom-document-image-import.md b/errors/custom-document-image-import.md new file mode 100644 index 000000000000..64ff9af1e8fb --- /dev/null +++ b/errors/custom-document-image-import.md @@ -0,0 +1,46 @@ +# Images cannot be imported into your custom document. + +#### Why This Error Occurred + +An attempt to import an image file into [`pages/_document.js`](https://nextjs.org/docs/advanced-features/custom-document) was made. + +Custom documents aren't compiled for the browser and images statically imported like this will not be displayed. + +#### Possible Ways to Fix It + +If your image needs to be displayed on every page you can relocate it to your [`pages/_app.js`](https://nextjs.org/docs/advanced-features/custom-app) file. + +#### Example + +```jsx +//pages/_app.js +import yourImage from "path/to/your/image" +import Image from "next/image" + +function MyApp({ Component, pageProps }) { + return ( + <> + <Image src={yourImage} alt="your_image_description" /> + <Component {...pageProps} /> + </> +} + +export default MyApp +``` + +If your application is not using image imports with `next/image`, you can disable the built-in loader with the following next.config.js: + +```js +module.exports = { + images: { + disableStaticImages: true, + }, +} +``` + +### Useful Links + +- [Custom `Document`](https://nextjs.org/docs/advanced-features/custom-document) +- [Custom `App`](https://nextjs.org/docs/advanced-features/custom-app) +- [Static File Serving](https://nextjs.org/docs/basic-features/static-file-serving) +- [Disable Static Image Imports](https://nextjs.org/docs/api-reference/next/image#disable-static-imports) diff --git a/errors/deleting-query-params-in-middlewares.md b/errors/deleting-query-params-in-middlewares.md new file mode 100644 index 000000000000..847efbb0bddc --- /dev/null +++ b/errors/deleting-query-params-in-middlewares.md @@ -0,0 +1,35 @@ +# Deleting Query Parameters In Middlewares + +#### Why This Error Occurred + +In previous versions of Next.js, we were merging query parameters with the incoming request for rewrites happening in middlewares, to match the behavior of static rewrites declared in the config. This forced Next.js users to use empty query parameters values to delete keys. + +We are changing this behavior to allow extra flexibility and a more streamlined experience for users. So from now on, query parameters will not be merged and thus the warning. + +```typescript +import type { NextRequest } from 'next/server' +import { NextResponse } from 'next/server' + +export default function middleware(request: NextRequest) { + const nextUrl = request.nextUrl + nextUrl.searchParams.delete('key') // this is now possible! 🎉 + return NextResponse.rewrite(nextUrl) +} +``` + +#### Possible Ways to Fix It + +If you are relying on the old behavior, please add the query parameters manually to the rewritten URL. Using `request.nextUrl` would do that automatically for you. + +```typescript +import type { NextRequest } from 'next/server' +import { NextResponse } from 'next/server' + +export default function middleware(request: NextRequest) { + const nextUrl = request.nextUrl + nextUrl.pathname = '/dest' + return NextResponse.rewrite(nextUrl) +} +``` + +This warning will be removed in a next version of Next.js. diff --git a/errors/deprecated-target-config.md b/errors/deprecated-target-config.md new file mode 100644 index 000000000000..d43a94453611 --- /dev/null +++ b/errors/deprecated-target-config.md @@ -0,0 +1,13 @@ +# Deprecated target config + +#### Why This Error Occurred + +The `target` property in `next.config.js` has been deprecated. Please migrate to leverage the default target instead. + +#### Possible Ways to Fix It + +For serverless cases, leverage the new output file traces or deploy your application somewhere where they are leveraged automatically like [Vercel](https://vercel.com). + +### Useful Links + +- [Output File Tracing Documentation](https://nextjs.org/docs/advanced-features/output-file-tracing) diff --git a/errors/experimental-jest-transformer.md b/errors/experimental-jest-transformer.md new file mode 100644 index 000000000000..c790e177d458 --- /dev/null +++ b/errors/experimental-jest-transformer.md @@ -0,0 +1,7 @@ +# "next/jest" Experimental + +#### Why This Message Occurred + +You are using `next/jest` which is currently an experimental feature of Next.js. In a future version of Next.js `next/jest` will be marked as stable. + +If you have any feedback about the transformer you can share it on this discussion: https://github.com/vercel/next.js/discussions/31152. diff --git a/errors/export-image-api.md b/errors/export-image-api.md index dae959c90dca..caedb0679147 100644 --- a/errors/export-image-api.md +++ b/errors/export-image-api.md @@ -2,7 +2,7 @@ #### Why This Error Occurred -You are attempting to run `next export` while importing the `next/image` component configured using the default `loader`. +You are attempting to run `next export` while importing the `next/image` component using the default `loader` configuration. However, the default `loader` relies on the Image Optimization API which is not available for exported applications. @@ -10,15 +10,15 @@ This is because Next.js optimizes images on-demand, as users request them (not a #### Possible Ways to Fix It -- Use `next start` to run a server, which includes the Image Optimization API. -- Use any provider which supports Image Optimization (like [Vercel](https://vercel.com/docs/next.js/image-optimization)). -- Configure a third-party [loader](https://nextjs.org/docs/basic-features/image-optimization#loader) in `next.config.js`. -- Use the [`loader`](https://nextjs.org/docs/api-reference/next/image#loader) prop for `next/image`. +- Use [`next start`](https://nextjs.org/docs/api-reference/cli#production) to run a server, which includes the Image Optimization API. +- Use any provider which supports Image Optimization (such as [Vercel](https://vercel.com)). +- [Configure the loader](https://nextjs.org/docs/api-reference/next/image#loader-configuration) in `next.config.js`. +- Use the [`loader`](https://nextjs.org/docs/api-reference/next/image#loader) prop for each instance of `next/image`. ### Useful Links -- [Deployment Documentation](https://nextjs.org/docs/deployment#vercel-recommended) +- [Deployment Documentation](https://nextjs.org/docs/deployment#managed-nextjs-with-vercel) - [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) - [`next export` Documentation](https://nextjs.org/docs/advanced-features/static-html-export) - [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) -- [Vercel Documentation](https://vercel.com/docs/next.js/image-optimization) +- [Vercel Documentation](https://vercel.com/docs/concepts/next.js/image-optimization) diff --git a/errors/failed-loading-swc.md b/errors/failed-loading-swc.md new file mode 100644 index 000000000000..8dad371dd4cb --- /dev/null +++ b/errors/failed-loading-swc.md @@ -0,0 +1,26 @@ +# SWC Failed to Load + +#### Why This Message Occurred + +Next.js now uses Rust-based compiler [SWC](https://swc.rs/) to compile JavaScript/TypeScript. This new compiler is up to 17x faster than Babel when compiling individual files and up to 5x faster Fast Refresh. + +SWC requires a binary be downloaded that is compatible specific to your system. In some cases this binary may fail to load either from failing to download or an incompatibility with your architecture. + +#### Possible Ways to Fix It + +You might need to allow optional packages to be installed by your package manager (remove `--no-optional` flag) for the package to download correctly. + +If SWC continues to fail to load you can opt-out by disabling `swcMinify` in your `next.config.js` or by adding a `.babelrc` to your project with the following content: + +```json +{ + "presets": ["next/babel"] +} +``` + +Be sure to report the issue on [the feedback thread](https://github.com/vercel/next.js/discussions/30468) so that we can investigate it further. + +### Useful Links + +- [SWC Feedback Thread](https://github.com/vercel/next.js/discussions/30468) +- [SWC Disabled Document](https://nextjs.org/docs/messages/swc-disabled) diff --git a/errors/get-initial-props-as-an-instance-method.md b/errors/get-initial-props-as-an-instance-method.md index 26218c66e28e..805055202f40 100644 --- a/errors/get-initial-props-as-an-instance-method.md +++ b/errors/get-initial-props-as-an-instance-method.md @@ -36,4 +36,4 @@ export default YourEntryComponent ### Useful Links -- [Fetching data and component lifecycle](https://nextjs.org/docs/api-reference/data-fetching/getInitialProps) +- [Fetching data and component lifecycle](https://nextjs.org/docs/api-reference/data-fetching/get-initial-props) diff --git a/errors/google-font-preconnect.md b/errors/google-font-preconnect.md index de629ac4e1bf..d95d72dba9bd 100644 --- a/errors/google-font-preconnect.md +++ b/errors/google-font-preconnect.md @@ -12,6 +12,9 @@ Add `rel="preconnect"` to the Google Font domain `<link>` tag: <link rel="preconnect" href="https://fonts.gstatic.com" /> ``` +Note: a **separate** link with `dns-prefetch` can be used as a fallback for browsers that don't support `preconnect` although this is not required. + ### Useful Links - [Preconnect to required origins](https://web.dev/uses-rel-preconnect/) +- [Preconnect and dns-prefetch](https://web.dev/preconnect-and-dns-prefetch/#resolve-domain-name-early-with-reldns-prefetch) diff --git a/errors/gsp-redirect-during-prerender.md b/errors/gsp-redirect-during-prerender.md index 964d7bf1db1f..3246e0464df9 100644 --- a/errors/gsp-redirect-during-prerender.md +++ b/errors/gsp-redirect-during-prerender.md @@ -10,4 +10,4 @@ Remove any paths that result in a redirect from being prerendered in `getStaticP ### Useful Links -- [Data Fetching Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) +- [Data Fetching Documentation](/docs/basic-features/data-fetching/get-static-props.md) diff --git a/errors/gssp-export.md b/errors/gssp-export.md index 62909cabf4e3..3110e974f396 100644 --- a/errors/gssp-export.md +++ b/errors/gssp-export.md @@ -33,6 +33,6 @@ The `getServerSideProps` lifecycle is not compatible with `next export`, so you' ### Useful Links -- [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization) -- [`getStaticProps` documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) -- [`exportPathMap` documentation](https://nextjs.org/docs/api-reference/next.config.js/exportPathMap) +- [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) +- [`getStaticProps` documentation](/docs/basic-features/data-fetching/get-static-props.md) +- [`exportPathMap` documentation](/docs/api-reference/next.config.js/exportPathMap.md) diff --git a/errors/gssp-mixed-not-found-redirect.md b/errors/gssp-mixed-not-found-redirect.md index 5f592ce610c6..9765365a8394 100644 --- a/errors/gssp-mixed-not-found-redirect.md +++ b/errors/gssp-mixed-not-found-redirect.md @@ -12,5 +12,5 @@ Make sure only `notFound` **or** `redirect` is being returned on your page's `ge ### Useful Links -- [`getStaticProps` Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) -- [`getServerSideProps` Documentation](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) +- [`getStaticProps` Documentation](/docs/basic-features/data-fetching/get-static-props.md) +- [`getServerSideProps` Documentation](/docs/basic-features/data-fetching/get-server-side-props.md) diff --git a/errors/gssp-no-mutating-res.md b/errors/gssp-no-mutating-res.md new file mode 100644 index 000000000000..d40db77d6ecb --- /dev/null +++ b/errors/gssp-no-mutating-res.md @@ -0,0 +1,19 @@ +# Must not access ServerResponse after getServerSideProps() resolves + +#### Why This Error Occurred + +`getServerSideProps()` surfaces a `ServerResponse` object through the `res` property of its `context` arg. This object is not intended to be accessed or changed after `getServerSideProps()` resolves. + +This is because the framework tries to optimize when items like headers or status codes are flushed to the browser. If they are changed after `getServerSideProps()` completes, we can't guarantee that the changes will work. + +For this reason, accessing the object after this time is disallowed. + +#### Possible Ways to Fix It + +You can fix this error by moving any access of the `res` object into `getServerSideProps()` itself or any functions that run before `getServerSideProps()` returns. + +If you’re using a custom server and running into this problem due to session middleware like `next-session` or `express-session`, try installing the middleware in the server instead of `getServerSideProps()`. + +### Useful Links + +- [Data Fetching Docs](https://nextjs.org/docs/basic-features/data-fetching) diff --git a/errors/ignored-compiler-options.md b/errors/ignored-compiler-options.md new file mode 100644 index 000000000000..edab33451d6e --- /dev/null +++ b/errors/ignored-compiler-options.md @@ -0,0 +1,9 @@ +# ignored-compiler-options + +#### Why This Error Occurred + +Options under the `compiler` key in `next.config.js` only apply to the new Rust based compiler and will be ignored if Babel is used instead. This message will appear if Next.js detects a Babel configuration file while `compiler` options are configured in `next.config.js` + +### Useful Links + +- [Next.js Compiler](https://nextjs.org/docs/advanced-features/compiler) diff --git a/errors/invalid-api-status-body.md b/errors/invalid-api-status-body.md index b0d5fd60fd99..45f86c0b3684 100644 --- a/errors/invalid-api-status-body.md +++ b/errors/invalid-api-status-body.md @@ -1,4 +1,4 @@ -Invalid API Route Status/Body Response +# Invalid API Route Status/Body Response #### Why This Error Occurred @@ -22,7 +22,7 @@ After ```js export default function handler(req, res) { - res.status(204).send() + res.status(204).end() } ``` diff --git a/errors/invalid-dynamic-options-type.md b/errors/invalid-dynamic-options-type.md new file mode 100644 index 000000000000..e5aa443ac58e --- /dev/null +++ b/errors/invalid-dynamic-options-type.md @@ -0,0 +1,31 @@ +# Invalid options type in a `next/dynamic` call + +#### Why This Error Occurred + +You have an invalid options type in a `next/dynamic` call. The options must be an object literal. + +#### Possible Ways to Fix It + +**Before** + +```jsx +import dynamic from 'next/dynamic' + +const options = { loading: () => <p>...</p>, ssr: false } +const DynamicComponent = dynamic(() => import('../components/hello'), options) +``` + +**After** + +```jsx +import dynamic from 'next/dynamic' + +const DynamicComponent = dynamic(() => import('../components/hello'), { + loading: () => <p>...</p>, + ssr: false, +}) +``` + +### Useful Links + +- [Dynamic Import](https://nextjs.org/docs/advanced-features/dynamic-import) diff --git a/errors/invalid-getstaticpaths-value.md b/errors/invalid-getstaticpaths-value.md index f1e8543d9503..f34640ba00af 100644 --- a/errors/invalid-getstaticpaths-value.md +++ b/errors/invalid-getstaticpaths-value.md @@ -35,6 +35,7 @@ There are two required properties: } } ``` -1. `fallback`: this property is a [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean), specifying whether or not a fallback version of this page should be generated. +1. `fallback`: this property can be a [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean), specifying whether or not a fallback version of this page should be generated, or a string `'blocking'` to wait for the generation: - Enabling `fallback` (via `true`) allows you to return a subset of all the possible paths that should be statically generated. At runtime, Next.js will statically generate the remaining paths the **first time they are requested**. Consecutive calls to the path will be served as-if it was statically generated at build-time. This reduces build times when dealing with thousands or millions of pages. - Disabling `fallback` (via `false`) requires you return the full collection of paths you would like to statically generate at build-time. At runtime, any path that was not generated at build-time **will 404**. + - If `fallback` is `'blocking'`, new paths not returned by getStaticPaths will wait for the HTML to be generated, identical to SSR (hence why blocking), and then be cached for future requests so it only happens once per path. diff --git a/errors/invalid-images-config.md b/errors/invalid-images-config.md index febbc5ee4ada..581724439222 100644 --- a/errors/invalid-images-config.md +++ b/errors/invalid-images-config.md @@ -17,6 +17,7 @@ module.exports = { imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], // limit of 50 domains values domains: [], + // path prefix for Image Optimization API, useful with `loader` path: '/_next/image', // loader can be 'default', 'imgix', 'cloudinary', 'akamai', or 'custom' loader: 'default', @@ -24,6 +25,8 @@ module.exports = { disableStaticImages: false, // minimumCacheTTL is in seconds, must be integer 0 or more minimumCacheTTL: 60, + // ordered list of acceptable optimized image formats (mime types) + formats: ['image/webp'], }, } ``` @@ -31,3 +34,4 @@ module.exports = { ### Useful Links - [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) +- [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) diff --git a/errors/invalid-project-dir-casing.md b/errors/invalid-project-dir-casing.md new file mode 100644 index 000000000000..8f650f95acac --- /dev/null +++ b/errors/invalid-project-dir-casing.md @@ -0,0 +1,16 @@ +# Invalid Project Directory Casing + +#### Why This Error Occurred + +When starting Next.js, the current directory is a different casing than the actual directory on your filesystem. This can cause files to resolve inconsistently. + +This can occur when using a case-insensitive filesystem. For example, opening PowerShell on Windows navigating to `cd path/to/myproject` instead of `cd path/to/MyProject`. + +#### Possible Ways to Fix It + +Ensure the casing for the current working directory matches the actual case of the real directory. Use a terminal that enforces case-sensitivity. + +### Useful Links + +- [Next.js CLI documentation](https://nextjs.org/docs/api-reference/cli) +- [Case sensitivity in filesystems](https://en.wikipedia.org/wiki/Case_sensitivity#In_filesystems) diff --git a/errors/invalid-redirect-gssp.md b/errors/invalid-redirect-gssp.md index 5b966ffa0a2d..7c17f2c416d4 100644 --- a/errors/invalid-redirect-gssp.md +++ b/errors/invalid-redirect-gssp.md @@ -29,4 +29,4 @@ export const getStaticProps = ({ params }) => { ### Useful Links -- [Data Fetching Documentation](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) +- [Data Fetching Documentation](/docs/basic-features/data-fetching/get-static-props.md) diff --git a/errors/invalid-styled-jsx-children.md b/errors/invalid-styled-jsx-children.md new file mode 100644 index 000000000000..86c0b3447f57 --- /dev/null +++ b/errors/invalid-styled-jsx-children.md @@ -0,0 +1,29 @@ +# Invalid `styled-jsx` children + +#### Why This Error Occurred + +You have passed invalid children to a `<style jsx>` tag. + +#### Possible Ways to Fix It + +Make sure to only pass one child to the `<style jsx>` tag, typically a template literal. + +```jsx +const Component = () => ( + <div> + <p>Red paragraph</p> + <style jsx>{` + p { + color: red; + } + `}</style> + </div> +) +``` + +Please see the links for more examples. + +### Useful Links + +- [Built-In CSS-in-JS](https://nextjs.org/docs/basic-features/built-in-css-support#css-in-js) +- [styled-jsx documentation](https://github.com/vercel/styled-jsx) diff --git a/errors/large-page-data.md b/errors/large-page-data.md new file mode 100644 index 000000000000..62600879b441 --- /dev/null +++ b/errors/large-page-data.md @@ -0,0 +1,13 @@ +# Large Page Data + +#### Why This Error Occurred + +One of your pages includes a large amount of page data (>= 128KB). This can negatively impact performance since page data must be parsed by the client before the page is hydrated. + +#### Possible Ways to Fix It + +Reduce the amount of data returned from `getStaticProps`, `getServerSideProps`, or `getInitialProps` to only the essential data to render the page. + +### Useful Links + +- [Data Fetching Documentation](https://nextjs.org/docs/basic-features/data-fetching) diff --git a/errors/manifest.json b/errors/manifest.json index 693fb8f05514..a7e0bc694424 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -4,11 +4,38 @@ "title": "Messages", "heading": true, "routes": [ + { + "title": "react-hydration-error", + "path": "/errors/react-hydration-error.md" + }, + { + "title": "beta-middleware", + "path": "/errors/beta-middleware.md" + }, + { + "title": "failed-loading-swc", + "path": "/errors/failed-loading-swc.md" + }, + { + "title": "deprecated-target-config", + "path": "/errors/deprecated-target-config.md" + }, + { + "title": "custom-document-image-import", + "path": "/errors/custom-document-image-import.md" + }, + { + "title": "large-page-data", + "path": "/errors/large-page-data.md" + }, { "title": "404-get-initial-props", "path": "/errors/404-get-initial-props.md" }, - { "title": "amp-bind-jsx-alt", "path": "/errors/amp-bind-jsx-alt.md" }, + { + "title": "amp-bind-jsx-alt", + "path": "/errors/amp-bind-jsx-alt.md" + }, { "title": "amp-export-validation", "path": "/errors/amp-export-validation.md" @@ -65,9 +92,18 @@ "title": "conflicting-ssg-paths", "path": "/errors/conflicting-ssg-paths.md" }, - { "title": "css-global", "path": "/errors/css-global.md" }, - { "title": "css-modules-npm", "path": "/errors/css-modules-npm.md" }, - { "title": "css-npm", "path": "/errors/css-npm.md" }, + { + "title": "css-global", + "path": "/errors/css-global.md" + }, + { + "title": "css-modules-npm", + "path": "/errors/css-modules-npm.md" + }, + { + "title": "css-npm", + "path": "/errors/css-npm.md" + }, { "title": "custom-error-no-custom-404", "path": "/errors/custom-error-no-custom-404.md" @@ -76,7 +112,10 @@ "title": "doc-crossorigin-deprecated", "path": "/errors/doc-crossorigin-deprecated.md" }, - { "title": "duplicate-sass", "path": "/errors/duplicate-sass.md" }, + { + "title": "duplicate-sass", + "path": "/errors/duplicate-sass.md" + }, { "title": "empty-configuration", "path": "/errors/empty-configuration.md" @@ -97,7 +136,10 @@ "title": "export-all-in-page", "path": "/errors/export-all-in-page.md" }, - { "title": "export-image-api", "path": "/errors/export-image-api.md" }, + { + "title": "export-image-api", + "path": "/errors/export-image-api.md" + }, { "title": "export-no-custom-routes", "path": "/errors/export-no-custom-routes.md" @@ -130,17 +172,30 @@ "title": "gssp-component-member", "path": "/errors/gssp-component-member.md" }, - { "title": "gssp-export", "path": "/errors/gssp-export.md" }, + { + "title": "gssp-export", + "path": "/errors/gssp-export.md" + }, { "title": "gssp-mixed-not-found-redirect", "path": "/errors/gssp-mixed-not-found-redirect.md" }, - { "title": "head-build-id", "path": "/errors/head-build-id.md" }, + { + "title": "gssp-no-mutating-res", + "path": "/errors/gssp-no-mutating-res.md" + }, + { + "title": "head-build-id", + "path": "/errors/head-build-id.md" + }, { "title": "href-interpolation-failed", "path": "/errors/href-interpolation-failed.md" }, - { "title": "improper-devtool", "path": "/errors/improper-devtool.md" }, + { + "title": "improper-devtool", + "path": "/errors/improper-devtool.md" + }, { "title": "incompatible-href-as", "path": "/errors/incompatible-href-as.md" @@ -149,8 +204,14 @@ "title": "inline-script-id", "path": "/errors/inline-script-id.md" }, - { "title": "install-sass", "path": "/errors/install-sass.md" }, - { "title": "install-sharp", "path": "/errors/install-sharp.md" }, + { + "title": "install-sass", + "path": "/errors/install-sass.md" + }, + { + "title": "install-sharp", + "path": "/errors/install-sharp.md" + }, { "title": "invalid-assetprefix", "path": "/errors/invalid-assetprefix.md" @@ -223,7 +284,10 @@ "title": "link-passhref", "path": "/errors/link-passhref.md" }, - { "title": "manifest.json", "path": "/errors/manifest.json" }, + { + "title": "manifest.json", + "path": "/errors/manifest.json" + }, { "title": "minification-disabled", "path": "/errors/minification-disabled.md" @@ -236,7 +300,10 @@ "title": "missing-env-value", "path": "/errors/missing-env-value.md" }, - { "title": "multi-tabs", "path": "/errors/multi-tabs.md" }, + { + "title": "multi-tabs", + "path": "/errors/multi-tabs.md" + }, { "title": "nested-reserved-page", "path": "/errors/nested-reserved-page.md" @@ -277,8 +344,14 @@ "title": "next-start-serverless", "path": "/errors/next-start-serverless.md" }, - { "title": "no-cache", "path": "/errors/no-cache.md" }, - { "title": "no-css-tags", "path": "/errors/no-css-tags.md" }, + { + "title": "no-cache", + "path": "/errors/no-cache.md" + }, + { + "title": "no-css-tags", + "path": "/errors/no-css-tags.md" + }, { "title": "no-document-import-in-page", "path": "/errors/no-document-import-in-page.md" @@ -315,6 +388,10 @@ "title": "no-router-instance", "path": "/errors/no-router-instance.md" }, + { + "title": "no-server-import-in-page", + "path": "/errors/no-server-import-in-page.md" + }, { "title": "no-sync-scripts", "path": "/errors/no-sync-scripts.md" @@ -347,17 +424,26 @@ "title": "popstate-state-empty", "path": "/errors/popstate-state-empty.md" }, - { "title": "postcss-function", "path": "/errors/postcss-function.md" }, + { + "title": "postcss-function", + "path": "/errors/postcss-function.md" + }, { "title": "postcss-ignored-plugin", "path": "/errors/postcss-ignored-plugin.md" }, - { "title": "postcss-shape", "path": "/errors/postcss-shape.md" }, + { + "title": "postcss-shape", + "path": "/errors/postcss-shape.md" + }, { "title": "prefetch-true-deprecated", "path": "/errors/prefetch-true-deprecated.md" }, - { "title": "prerender-error", "path": "/errors/prerender-error.md" }, + { + "title": "prerender-error", + "path": "/errors/prerender-error.md" + }, { "title": "production-start-no-build-id", "path": "/errors/production-start-no-build-id.md" @@ -370,7 +456,10 @@ "title": "public-next-folder-conflict", "path": "/errors/public-next-folder-conflict.md" }, - { "title": "react-version", "path": "/errors/react-version.md" }, + { + "title": "react-version", + "path": "/errors/react-version.md" + }, { "title": "render-no-starting-slash", "path": "/errors/render-no-starting-slash.md" @@ -395,13 +484,22 @@ "title": "static-dir-deprecated", "path": "/errors/static-dir-deprecated.md" }, - { "title": "threw-undefined", "path": "/errors/threw-undefined.md" }, + { + "title": "threw-undefined", + "path": "/errors/threw-undefined.md" + }, { "title": "undefined-webpack-config", "path": "/errors/undefined-webpack-config.md" }, - { "title": "url-deprecated", "path": "/errors/url-deprecated.md" }, - { "title": "webpack5", "path": "/errors/webpack5.md" }, + { + "title": "url-deprecated", + "path": "/errors/url-deprecated.md" + }, + { + "title": "webpack5", + "path": "/errors/webpack5.md" + }, { "title": "client-side-exception-occurred", "path": "/errors/client-side-exception-occurred.md" @@ -414,7 +512,14 @@ "title": "link-multiple-children", "path": "/errors/link-multiple-children.md" }, - { "title": "no-img-element", "path": "/errors/no-img-element.md" }, + { + "title": "no-img-element", + "path": "/errors/no-img-element.md" + }, + { + "title": "no-head-element", + "path": "/errors/no-head-element.md" + }, { "title": "non-dynamic-getstaticpaths-usage", "path": "/errors/non-dynamic-getstaticpaths-usage.md" @@ -439,13 +544,25 @@ "title": "sharp-missing-in-production", "path": "/errors/sharp-missing-in-production.md" }, + { + "title": "sharp-version-avif", + "path": "/errors/sharp-version-avif.md" + }, { "title": "script-in-document-page", "path": "/errors/no-script-in-document-page.md" }, { - "title": "script-in-head-component", - "path": "/errors/no-script-in-head-component.md" + "title": "script-component-in-head-component", + "path": "/errors/no-script-component-in-head-component.md" + }, + { + "title": "script-tags-in-head-component", + "path": "/errors/no-script-tags-in-head-component.md" + }, + { + "title": "stylesheets-in-head-component", + "path": "/errors/no-stylesheets-in-head-component.md" }, { "title": "max-custom-routes-reached", @@ -462,6 +579,46 @@ { "title": "invalid-api-status-body", "path": "/errors/invalid-api-status-body.md" + }, + { + "title": "invalid-project-dir-casing", + "path": "/errors/invalid-project-dir-casing.md" + }, + { + "title": "swc-disabled", + "path": "/errors/swc-disabled.md" + }, + { + "title": "swc-minify-enabled", + "path": "/errors/swc-minify-enabled.md" + }, + { + "title": "middleware-new-signature", + "path": "/errors/middleware-new-signature.md" + }, + { + "title": "experimental-jest-transformer", + "path": "/errors/experimental-jest-transformer.md" + }, + { + "title": "invalid-dynamic-options-type", + "path": "/errors/invalid-dynamic-options-type.md" + }, + { + "title": "invalid-styled-jsx-children", + "path": "/errors/invalid-styled-jsx-children.md" + }, + { + "title": "middleware-relative-urls", + "path": "/errors/middleware-relative-urls.md" + }, + { + "title": "deleting-query-params-in-middlewares", + "path": "/errors/deleting-query-params-in-middlewares.md" + }, + { + "title": "ignored-compiler-options", + "path": "/errors/ignored-compiler-options.md" } ] } diff --git a/errors/middleware-new-signature.md b/errors/middleware-new-signature.md new file mode 100644 index 000000000000..b7bb43307b5c --- /dev/null +++ b/errors/middleware-new-signature.md @@ -0,0 +1,37 @@ +# Deprecated Middleware API Signature + +#### Why This Error Occurred + +Your application is using a Middleware function that is using parameters from the deprecated API. + +```typescript +// _middleware.js +import { NextResponse } from 'next/server' + +export function middleware(event) { + if (event.request.nextUrl.pathname === '/blocked') { + event.respondWith( + new NextResponse(null, { + status: 403, + }) + ) + } +} +``` + +#### Possible Ways to Fix It + +Update to use the new API for Middleware: + +```typescript +// _middleware.js +import { NextResponse } from 'next/server' + +export function middleware(request) { + if (request.nextUrl.pathname === '/blocked') { + return new NextResponse(null, { + status: 403, + }) + } +} +``` diff --git a/errors/middleware-relative-urls.md b/errors/middleware-relative-urls.md new file mode 100644 index 000000000000..dabdf3306085 --- /dev/null +++ b/errors/middleware-relative-urls.md @@ -0,0 +1,38 @@ +# Middleware Relative URLs + +#### Why This Error Occurred + +You are using a Middleware function that uses `Response.redirect(url)`, `NextResponse.redirect(url)` or `NextResponse.rewrite(url)` where `url` is a relative or an invalid URL. Currently this will work, but building a request with `new Request(url)` or running `fetch(url)` when `url` is a relative URL will **not** work. For this reason and to bring consistency to Next.js Middleware, this behavior will be deprecated soon in favor of always using absolute URLs. + +#### Possible Ways to Fix It + +To fix this warning you must always pass absolute URL for redirecting and rewriting. There are several ways to get the absolute URL but the recommended way is to clone `NextURL` and mutate it: + +```typescript +import type { NextRequest } from 'next/server' +import { NextResponse } from 'next/server' + +export function middleware(request: NextRequest) { + const url = request.nextUrl.clone() + url.pathname = '/dest' + return NextResponse.rewrite(url) +} +``` + +Another way to fix this error could be to use the original URL as the base but this will not consider configuration like `basePath` or `locale`: + +```typescript +import type { NextRequest } from 'next/server' +import { NextResponse } from 'next/server' + +export function middleware(request: NextRequest) { + return NextResponse.rewrite(new URL('/dest', request.url)) +} +``` + +You can also pass directly a string containing a valid absolute URL. + +### Useful Links + +- [URL Documentation](https://developer.mozilla.org/en-US/docs/Web/API/URL) +- [Response Documentation](https://developer.mozilla.org/en-US/docs/Web/API/Response) diff --git a/errors/next-config-error.md b/errors/next-config-error.md index d5e5b8b14638..e30b02517738 100644 --- a/errors/next-config-error.md +++ b/errors/next-config-error.md @@ -2,13 +2,15 @@ #### Why This Error Occurred -When attempting to load your `next.config.js` file an error occurred. This could be due to a syntax error or attempting to `require` a module that wasn't available. +When attempting to load your `next.config.js` or `next.config.mjs` file, an error occurred. This could be due to a syntax error or attempting to `require`/`import` a module that wasn't available. #### Possible Ways to Fix It -See the error message in your terminal where you started `next` to see more context. The `next.config.js` file is not transpiled by Next.js currently so ensure only features supported by your current node.js version are being used. +See the error message in your terminal where you started `next` to see more context. + +> Note: This config file is not transpiled by Next.js, so only use features supported by your current Node.js version. ### Useful Links - [next.config.js documentation](https://nextjs.org/docs/api-reference/next.config.js/introduction) -- [node.js version feature chart](https://node.green/) +- [Node.js version feature chart](https://node.green/) diff --git a/errors/next-script-for-ga.md b/errors/next-script-for-ga.md index 2f367dff127c..0785fcca1770 100644 --- a/errors/next-script-for-ga.md +++ b/errors/next-script-for-ga.md @@ -13,15 +13,15 @@ If you are using the [gtag.js](https://developers.google.com/analytics/devguides ```jsx import Script from 'next/script' -const Home = () => { +function Home() { return ( <div class="container"> <!-- Global site tag (gtag.js) - Google Analytics --> <Script src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID" - strategy="lazyOnload" + strategy="afterInteractive" /> - <Script id="google-analytics"> + <Script id="google-analytics" strategy="afterInteractive"> {` window.dataLayer = window.dataLayer || []; function gtag(){window.dataLayer.push(arguments);} @@ -44,10 +44,10 @@ If you are using the [analytics.js](https://developers.google.com/analytics/devg ```jsx import Script from 'next/script' -const Home = () => { +function Home() { return ( <div class="container"> - <Script id="google-analytics"> + <Script id="google-analytics" strategy="afterInteractive"> {` (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), @@ -70,10 +70,10 @@ If you are using the [alternative async variant](https://developers.google.com/a ```jsx import Script from 'next/script' -const Home = () => { +function Home() { return ( <div class="container"> - <Script id="google-analytics"> + <Script id="google-analytics" strategy="afterInteractive"> {` window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date; ga('create', 'GOOGLE_ANALYTICS_ID', 'auto'); @@ -82,7 +82,7 @@ const Home = () => { </Script> <Script src="https://www.google-analytics.com/analytics.js" - strategy="lazyOnload" + strategy="afterInteractive" /> </div> ) diff --git a/errors/no-cache.md b/errors/no-cache.md index a580397ef99b..b3b0214ca31f 100644 --- a/errors/no-cache.md +++ b/errors/no-cache.md @@ -60,7 +60,7 @@ cache: #### Netlify CI -Use [Netlify Plugins](https://www.netlify.com/products/build/plugins/) with [`netlify-plugin-cache-nextjs`](https://www.npmjs.com/package/netlify-plugin-cache-nextjs). +Use [Netlify Plugins](https://www.netlify.com/products/build/plugins/) with [`@netlify/plugin-nextjs`](https://www.npmjs.com/package/@netlify/plugin-nextjs). #### AWS CodeBuild @@ -80,9 +80,12 @@ Using GitHub's [actions/cache](https://github.com/actions/cache), add the follow ```yaml uses: actions/cache@v2 with: - path: ${{ github.workspace }}/.next/cache + # See here for caching with `yarn` https://github.com/actions/cache/blob/main/examples.md#node---yarn or you can leverage caching with actions/setup-node https://github.com/actions/setup-node + path: | + ~/.npm + ${{ github.workspace }}/.next/cache # Generate a new cache whenever packages or source files change. - key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]sx?') }} + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} # If source files changed but packages didn't, rebuild from a prior cache. restore-keys: | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- diff --git a/errors/no-head-element.md b/errors/no-head-element.md new file mode 100644 index 000000000000..670894e64a44 --- /dev/null +++ b/errors/no-head-element.md @@ -0,0 +1,30 @@ +# No Head Element + +### Why This Error Occurred + +An HTML `<head>` element was used to include page-level metadata, but this can cause unexpected behavior in a Next.js application. Use Next.js' built-in `<Head />` component instead. + +### Possible Ways to Fix It + +Import and use the `<Head />` component: + +```jsx +import Head from 'next/head' + +function Index() { + return ( + <> + <Head> + <title>My page title + + + + ) +} + +export default Index +``` + +### Useful Links + +- [next/head](https://nextjs.org/docs/api-reference/next/head) diff --git a/errors/no-html-link-for-pages.md b/errors/no-html-link-for-pages.md index 0fe1449d69a9..38457ae4a46a 100644 --- a/errors/no-html-link-for-pages.md +++ b/errors/no-html-link-for-pages.md @@ -53,7 +53,7 @@ In some cases, you may also need to configure this rule directly by providing a ```json { "rules": { - "@next/next/no-html-link-for-pages": ["error", "/my-app/pages/"] + "@next/next/no-html-link-for-pages": ["error", "packages/my-app/pages/"] } } ``` diff --git a/errors/no-img-element.md b/errors/no-img-element.md index 96826da88860..532dac586cb2 100644 --- a/errors/no-img-element.md +++ b/errors/no-img-element.md @@ -2,7 +2,7 @@ ### Why This Error Occurred -An HTML `` element was used to display an image. For better performance and automatic image optimization, use Next.js' built-in image component instead. +An HTML `` element was used to display an image. For better performance and automatic Image Optimization, use Next.js' built-in image component instead. ### Possible Ways to Fix It diff --git a/errors/no-page-custom-font.md b/errors/no-page-custom-font.md index 93766e1d5d36..edcea71d99b3 100644 --- a/errors/no-page-custom-font.md +++ b/errors/no-page-custom-font.md @@ -2,7 +2,8 @@ ### Why This Error Occurred -A custom font was added to a page and not with a custom `Document`. This only adds the font to the specific page and not to the entire application. +- The custom font you're adding was added to a page - this only adds the font to the specific page and not the entire application. +- The custom font you're adding was added to a separate component within `Document` - this disables automatic font optimization. ### Possible Ways to Fix It @@ -35,9 +36,34 @@ class MyDocument extends Document { export default MyDocument ``` +Or as a function component: + +```js +// pages/_document.js + +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + + + +
+ + + + ) +} +``` + ### When Not To Use It -If you have a reason to only load a font for a particular page, then you can disable this rule. +If you have a reason to only load a font for a particular page or don't care about font optimization, then you can disable this rule. ### Useful Links diff --git a/errors/no-script-in-head-component.md b/errors/no-script-component-in-head-component.md similarity index 100% rename from errors/no-script-in-head-component.md rename to errors/no-script-component-in-head-component.md diff --git a/errors/no-script-tags-in-head-component.md b/errors/no-script-tags-in-head-component.md new file mode 100644 index 000000000000..278ab907b892 --- /dev/null +++ b/errors/no-script-tags-in-head-component.md @@ -0,0 +1,36 @@ +# No Script Tags In Head Component + +### Why This Error Occurred + +A ` +
Home Page
+ + ) +} + +export default Home +``` + +### Useful Links + +- [Script component docs](https://nextjs.org/docs/basic-features/script/) diff --git a/errors/no-server-import-in-page.md b/errors/no-server-import-in-page.md new file mode 100644 index 000000000000..c8fc45069a28 --- /dev/null +++ b/errors/no-server-import-in-page.md @@ -0,0 +1,23 @@ +# No Server Import In Page + +### Why This Error Occurred + +`next/server` was imported outside of `pages/**/_middleware.{js,ts}`. + +### Possible Ways to Fix It + +Only import and use `next/server` in a file located within the pages directory: `pages/**/_middleware.{js,ts}`. + +```ts +// pages/_middleware.ts + +import type { NextFetchEvent, NextRequest } from 'next/server' + +export function middleware(req: NextRequest, ev: NextFetchEvent) { + return new Response('Hello, world!') +} +``` + +### Useful Links + +- [Middleware](https://nextjs.org/docs/middleware) diff --git a/errors/no-stylesheets-in-head-component.md b/errors/no-stylesheets-in-head-component.md new file mode 100644 index 000000000000..3e3901a682dd --- /dev/null +++ b/errors/no-stylesheets-in-head-component.md @@ -0,0 +1,42 @@ +# No Stylesheets In Head Component + +### Why This Error Occurred + +A `` tag was added using the `next/head` component. + +We don't recommend this pattern because it will potentially break when used with Suspense and/or streaming. In these contexts, `next/head` tags aren't: + +- guaranteed to be included in the initial SSR response, so loading could be delayed until client-side rendering, regressing performance. + +- loaded in any particular order. The order that the app's Suspense boundaries resolve will determine the loading order of your stylesheets. + +### Possible Ways to Fix It + +#### Document + +Add the stylesheet in a custom `Document` component. + +```jsx +// pages/_document.js +import Document, { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + + + +
+ + + + ) +} +``` + +Note that the functional syntax for `Document` above is preferred over the `class` syntax, so that it will be compatible with React Server Components down the line. + +### Useful Links + +- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document) diff --git a/errors/no-sync-scripts.md b/errors/no-sync-scripts.md index 72e9275b6914..b8a200e8b58d 100644 --- a/errors/no-sync-scripts.md +++ b/errors/no-sync-scripts.md @@ -13,7 +13,7 @@ Use the Script component with the right loading strategy to defer loading of the ```jsx import Script from 'next/script' -const Home = () => { +function Home() { return (
diff --git a/errors/page-data-collection-timeout.md b/errors/page-data-collection-timeout.md index 12332bfb430b..49d2d80fa9f6 100644 --- a/errors/page-data-collection-timeout.md +++ b/errors/page-data-collection-timeout.md @@ -11,8 +11,8 @@ When restarted it will retry all uncompleted jobs, but if a job was unsuccessful - Make sure that there is no infinite loop during execution. - Make sure all Promises in `getStaticPaths` `resolve` or `reject` correctly. - Avoid very long timeouts for network requests. -- Increase the timeout by changing the `experimental.pageDataCollectionTimeout` configuration option (default `60` in seconds). +- Increase the timeout by changing the `config.staticPageGenerationTimeout` configuration option (default `60` in seconds). ### Useful Links -- [`getStaticPaths`](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation) +- [`getStaticPaths`](/docs/basic-features/data-fetching/get-static-paths.md) diff --git a/errors/placeholder-blur-data-url.md b/errors/placeholder-blur-data-url.md index 2fc53099c983..9c9a76819dd7 100644 --- a/errors/placeholder-blur-data-url.md +++ b/errors/placeholder-blur-data-url.md @@ -6,7 +6,7 @@ You are attempting use the `next/image` component with `placeholder=blur` proper The `blurDataURL` might be missing because you're using a string for `src` instead of a static import. -Or `blurDataURL` might be missing because the static import is an unsupported image format. Only jpg, png, and webp are supported at this time. +Or `blurDataURL` might be missing because the static import is an unsupported image format. Only jpg, png, webp, and avif are supported at this time. #### Possible Ways to Fix It diff --git a/errors/prerender-error.md b/errors/prerender-error.md index 87e301c459db..474926c9631b 100644 --- a/errors/prerender-error.md +++ b/errors/prerender-error.md @@ -7,7 +7,8 @@ While prerendering a page an error occurred. This can occur for many reasons fro #### Possible Ways to Fix It - Make sure to move any non-pages out of the `pages` folder -- Check for any code that assumes a prop is available even when it might not be. e.g., have default data for all dynamic pages' props. +- Check for any code that assumes a prop is available, even when it might not be +- Set default values for all dynamic pages' props (avoid `undefined`, use `null` instead so it can be serialized) - Check for any out of date modules that you might be relying on -- Make sure your component handles `fallback` if it is enabled in `getStaticPaths`. [Fallback docs](https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required) -- Make sure you are not trying to export (`next export`) pages that have server-side rendering enabled [(getServerSideProps)](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) +- Make sure your component handles `fallback` if it is enabled in `getStaticPaths`. [Fallback docs](https://nextjs.org/docs/api-reference/data-fetching/get-static-paths#fallback-false) +- Make sure you are not trying to export (`next export`) pages that have server-side rendering enabled [(getServerSideProps)](/docs/basic-features/data-fetching/get-server-side-props.md) diff --git a/errors/promise-in-next-config.md b/errors/promise-in-next-config.md index f39ce7786fc7..84ac204fa85c 100644 --- a/errors/promise-in-next-config.md +++ b/errors/promise-in-next-config.md @@ -14,6 +14,8 @@ module.exports = { #### Possible Ways to Fix It -Check your `next.config.js` for `async` or `return Promise` +In Next.js versions above `12.0.10`, `module.exports = async () =>` is supported. + +For older versions, you can check your `next.config.js` for `async` or `return Promise`. Potentially a plugin is returning a `Promise` from the webpack function. diff --git a/errors/react-hydration-error.md b/errors/react-hydration-error.md new file mode 100644 index 000000000000..38ea8e05540b --- /dev/null +++ b/errors/react-hydration-error.md @@ -0,0 +1,53 @@ +# React Hydration Error + +#### Why This Error Occurred + +While rendering your application, there was a difference between the React tree that was pre-rendered (SSR/SSG) and the React tree that rendered during the first render in the Browser. The first render is called Hydration which is a [feature of React](https://reactjs.org/docs/react-dom.html#hydrate). + +This can cause the React tree to be out of sync with the DOM and result in unexpected content/attributes being present. + +#### Possible Ways to Fix It + +In general this issue is caused by using a specific library or application code that is relying on something that could differ between pre-rendering and the browser. An example of this is using `window` in a component's rendering. + +An example: + +```jsx +function MyComponent() { + // This condition depends on `window`. During the first render of the browser the `color` variable will be different + const color = typeof window !== 'undefined' ? 'red' : 'blue' + // As color is passed as a prop there is a mismatch between what was rendered server-side vs what was rendered in the first render + return

Hello World!

+} +``` + +How to fix it: + +```jsx +// In order to prevent the first render from being different you can use `useEffect` which is only executed in the browser and is executed during hydration +import { useEffect, useState } from 'react' +function MyComponent() { + // The default value is 'blue', it will be used during pre-rendering and the first render in the browser (hydration) + const [color, setColor] = useState('blue') + // During hydration `useEffect` is called. `window` is available in `useEffect`. In this case because we know we're in the browser checking for window is not needed. If you need to read something from window that is fine. + // By calling `setColor` in `useEffect` a render is triggered after hydrating, this causes the "browser specific" value to be available. In this case 'red'. + useEffect(() => setColor('red'), []) + // As color is a state passed as a prop there is no mismatch between what was rendered server-side vs what was rendered in the first render. After useEffect runs the color is set to 'red' + return

Hello World!

+} +``` + +Common causes with css-in-js libraries: + +- When using Styled Components / Emotion + - When css-in-js libraries are not set up for pre-rendering (SSR/SSG) it will often lead to a hydration mismatch. In general this means the application has to follow the Next.js example for the library. For example if `pages/_document` is missing and the Babel plugin is not added. + - Possible fix for Styled Components: https://github.com/vercel/next.js/tree/canary/examples/with-styled-components + - If you want to leverage Styled Components with the new Next.js Compiler in Next.js 12 there is an [experimental flag available](https://github.com/vercel/next.js/discussions/30174#discussion-3643870) + - Possible fix for Emotion: https://github.com/vercel/next.js/tree/canary/examples/with-emotion +- When using other css-in-js libraries + - Similar to Styled Components / Emotion css-in-js libraries generally need configuration specified in their examples in the [examples directory](https://github.com/vercel/next.js/tree/canary/examples) + +### Useful Links + +- [React Hydration Documentation](https://reactjs.org/docs/react-dom.html#hydrate) +- [Josh Comeau's article on React Hydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/) diff --git a/errors/rewrite-auto-export-fallback.md b/errors/rewrite-auto-export-fallback.md index a16fa7125639..af712a447757 100644 --- a/errors/rewrite-auto-export-fallback.md +++ b/errors/rewrite-auto-export-fallback.md @@ -10,7 +10,7 @@ Rewriting to these pages are not yet supported since rewrites are not available For fallback SSG pages you can add the page to the list of [prerendered paths](https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required). -For static dynamic routes, you will currently need to either rewrite to non-dynamic route or opt the page out of the static optimization with [`getServerSideProps`](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) +For static dynamic routes, you will currently need to either rewrite to non-dynamic route or opt the page out of the static optimization with [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md) ### Useful Links diff --git a/errors/sharp-missing-in-production.md b/errors/sharp-missing-in-production.md index 02face5fc054..96903efad9ca 100644 --- a/errors/sharp-missing-in-production.md +++ b/errors/sharp-missing-in-production.md @@ -16,3 +16,4 @@ You are seeing this error because Image Optimization in production mode (`next s ### Useful Links - [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) +- [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) diff --git a/errors/sharp-version-avif.md b/errors/sharp-version-avif.md new file mode 100644 index 000000000000..dcd90aea3662 --- /dev/null +++ b/errors/sharp-version-avif.md @@ -0,0 +1,24 @@ +# Sharp Version Does Not Support AVIF + +#### Why This Error Occurred + +The `next/image` component's default loader uses [`sharp`](https://www.npmjs.com/package/sharp) if its installed. + +You are seeing this error because you have an outdated version of [`sharp`](https://www.npmjs.com/package/sharp) installed that does not support the AVIF image format. + +AVIF support was added to [`sharp`](https://www.npmjs.com/package/sharp) in version 0.27.0 (December 2020) so your installed version is likely older. + +#### Possible Ways to Fix It + +- Install the latest version of `sharp` by running `yarn add sharp@latest` in your project directory +- If you're using the `NEXT_SHARP_PATH` environment variable, then update the `sharp` install referenced in that path, for example `cd "$NEXT_SHARP_PATH/../" && yarn add sharp@latest` +- If you cannot upgrade `sharp`, you can instead disable AVIF by configuring [`formats`](https://nextjs.org/docs/api-reference/next/image#image-formats) in your `next.config.js` + +After choosing an option above, reboot the server by running either `next dev` or `next start` for development or production respectively. + +> Note: This is not necessary for Vercel deployments, since `sharp` is installed automatically for you. + +### Useful Links + +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) +- [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) diff --git a/errors/static-page-generation-timeout.md b/errors/static-page-generation-timeout.md index 6cef054dc5dd..c8cc05e24f34 100644 --- a/errors/static-page-generation-timeout.md +++ b/errors/static-page-generation-timeout.md @@ -11,9 +11,9 @@ When restarted it will retry all uncompleted jobs, but if a job was unsuccessful - Make sure that there is no infinite loop during execution. - Make sure all Promises in `getStaticPaths`/`getStaticProps` `resolve` or `reject` correctly. - Avoid very long timeouts for network requests. -- Increase the timeout by changing the `experimental.staticPageGenerationTimeout` configuration option (default `60` in seconds). +- Increase the timeout by changing the `staticPageGenerationTimeout` configuration option (default `60` in seconds). ### Useful Links -- [`getStaticPaths`](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation) -- [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) +- [`getStaticPaths`](https://nextjs.org/docs/basic-features/data-fetching/get-static-paths.md) +- [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching/get-static-props.md) diff --git a/errors/swc-disabled.md b/errors/swc-disabled.md new file mode 100644 index 000000000000..701f260c785b --- /dev/null +++ b/errors/swc-disabled.md @@ -0,0 +1,17 @@ +# SWC disabled + +#### Why This Message Occurred + +Next.js now uses Rust-based compiler [SWC](https://swc.rs/) to compile JavaScript/TypeScript. This new compiler is up to 17x faster than Babel when compiling individual files and up to 5x faster Fast Refresh. + +Next.js provides full backwards compatibility with applications that have [custom Babel configuration](https://nextjs.org/docs/advanced-features/customizing-babel-config). All transformations that Next.js handles by default like styled-jsx and tree-shaking of `getStaticProps` / `getStaticPaths` / `getServerSideProps` have been ported to Rust. + +When an application has custom Babel configuration Next.js will automatically opt-out of using SWC for compiling JavaScript/Typescript and will fall back to using Babel in the same way that it was used in Next.js 11. + +Many of the integrations with external libraries that currently require custom Babel transformations will be ported to Rust-based SWC transforms in the near future. These include but are not limited to: + +- Styled Components +- Emotion +- Relay + +In order to prioritize transforms that will help you adopt SWC please provide your `.babelrc` on [the feedback thread](https://github.com/vercel/next.js/discussions/30174). diff --git a/errors/swc-minify-enabled.md b/errors/swc-minify-enabled.md new file mode 100644 index 000000000000..712affcf27a7 --- /dev/null +++ b/errors/swc-minify-enabled.md @@ -0,0 +1,7 @@ +# SWC minify enabled + +#### Why This Message Occurred + +The application has enabled `swcMinify` in `next.config.js`. By opting in minification will happen using the [SWC](https://swc.rs) minifier instead of Terser. This new minifier is 7x faster than Terser with comparable output. We're actively working on optimizing the output size and minification speed further. + +If you have feedback about the minification, please provide it on [the feedback thread](https://github.com/vercel/next.js/discussions/30237). diff --git a/errors/template.md b/errors/template.md deleted file mode 100644 index 711da0c52abf..000000000000 --- a/errors/template.md +++ /dev/null @@ -1,13 +0,0 @@ -# - -#### Why This Error Occurred - - - -#### Possible Ways to Fix It - - - -### Useful Links - - diff --git a/errors/template.txt b/errors/template.txt new file mode 100644 index 000000000000..3aa3e131acac --- /dev/null +++ b/errors/template.txt @@ -0,0 +1,13 @@ +# {{title}} + +#### Why This Error Occurred + + + +#### Possible Ways to Fix It + + + +### Useful Links + + diff --git a/errors/threw-undefined.md b/errors/threw-undefined.md index 0d4129ef4f11..4804488cafc4 100644 --- a/errors/threw-undefined.md +++ b/errors/threw-undefined.md @@ -1,9 +1,21 @@ -# Threw undefined in render +# Threw `undefined`/`null` #### Why This Error Occurred -Somewhere in your code you `throw` an `undefined` value. Since this isn't a valid error there isn't a stack trace. We show this error instead to let you know what to look for. +Somewhere in your code you `throw` an `undefined` or `null` value. Since this isn't a valid error there isn't a stack trace. We show this error instead to let you know what to look for. + +```js +function getData() { + let error + throw error +} + +function Page() { + const error = data?.error || null + throw error +} +``` #### Possible Ways to Fix It -Look in your pages and find where an error could be throwing `undefined` +Look in your pages and find where an error could be throwing `undefined` or `null` values and ensure `new Error()` is used instead. diff --git a/errors/webpack5.md b/errors/webpack5.md index b8c994c549e3..a7f0d6f72bcd 100644 --- a/errors/webpack5.md +++ b/errors/webpack5.md @@ -2,7 +2,9 @@ #### Why This Message Occurred -Next.js has adopted webpack 5 as the default for compilation. We've spent a lot of effort into ensuring the transition from webpack 4 to 5 will be as smooth as possible. For example Next.js now comes with both webpack 4 and 5 allowing you to adopt webpack 5 by adding a flag to your `next.config.js`: +Next.js has adopted webpack 5 as the default for compilation. We've spent a lot of effort into ensuring the transition from webpack 4 to 5 will be as smooth as possible. + +Your application currently has webpack 5 disabled using the `webpack5: false` flag which has been removed in Next.js 12: ```js module.exports = { @@ -26,9 +28,8 @@ In the past releases we have gradually rolled out webpack 5 to Next.js applicati - In Next.js 10.2 we automatically opted-in applications without custom webpack configuration in `next.config.js` - In Next.js 10.2 we automatically opted-in applications that do not have a `next.config.js` -- In Next.js 11 webpack 5 was enabled by default for all applications. You can still opt-out and use webpack 4 to help with backwards compatibility using `webpack5: false` in `next.config.js` - -In the next major version webpack 4 support will be removed. +- In Next.js 11 webpack 5 was enabled by default for all applications. You could still opt-out and use webpack 4 to help with backwards compatibility using `webpack5: false` in `next.config.js` +- In Next.js 12 webpack 4 support was removed. #### Custom webpack configuration @@ -36,7 +37,7 @@ In case you do have custom webpack configuration, either through custom plugins - When using `next-transpile-modules` make sure you use the latest version which includes [this patch](https://github.com/martpie/next-transpile-modules/pull/179) - When using `@zeit/next-css` / `@zeit/next-sass` make sure you use the [built-in CSS/Sass support](https://nextjs.org/docs/basic-features/built-in-css-support) instead -- When using `@zeit/next-preact` use [this example](https://github.com/vercel/next-plugins/tree/master/packages/next-preact) instead +- When using `@zeit/next-preact` use [this example](https://github.com/vercel/next.js/tree/canary/examples/using-preact) instead - When using `@zeit/next-source-maps` use the [built-in production Source Map support](https://nextjs.org/docs/advanced-features/source-maps) - When using webpack plugins make sure they're upgraded to the latest version, in most cases the latest version will include webpack 5 support. In some cases these upgraded webpack plugins will only support webpack 5. diff --git a/examples/analyze-bundles/README.md b/examples/analyze-bundles/README.md index 8aec1e56c79c..02e1673ccd3b 100644 --- a/examples/analyze-bundles/README.md +++ b/examples/analyze-bundles/README.md @@ -1,6 +1,6 @@ # Analyzer Bundles example -This example shows how to analyze the output bundles using [@next/bundle-analyzer](https://github.com/vercel/next.js/tree/master/packages/next-bundle-analyzer) +This example shows how to analyze the output bundles using [@next/bundle-analyzer](https://github.com/vercel/next.js/tree/canary/packages/next-bundle-analyzer) ## Preview diff --git a/examples/api-routes-apollo-server/README.md b/examples/api-routes-apollo-server/README.md index 9b4209908adb..5d1066048ddc 100644 --- a/examples/api-routes-apollo-server/README.md +++ b/examples/api-routes-apollo-server/README.md @@ -1,6 +1,6 @@ # Consume local Apollo GraphQL schema to create Static Generation export -Next.js ships with two forms of pre-rendering: [Static Generation](https://nextjs.org/docs/basic-features/pages#static-generation-recommended) and [Server-side Rendering](https://nextjs.org/docs/basic-features/pages#server-side-rendering). This example shows how to perform Static Generation using a local [Apollo GraphQL](https://www.apollographql.com/docs/apollo-server/) schema within [getStaticProps](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) and [getStaticPaths](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation). The end result is a Next.js application that uses one Apollo GraphQL schema to generate static pages at build time and also serve a GraphQL [API Route](https://nextjs.org/docs/api-routes/introduction) at runtime. +Next.js ships with two forms of pre-rendering: [Static Generation](https://nextjs.org/docs/basic-features/pages#static-generation-recommended) and [Server-side Rendering](https://nextjs.org/docs/basic-features/pages#server-side-rendering). This example shows how to perform Static Generation using a local [Apollo GraphQL](https://www.apollographql.com/docs/apollo-server/) schema within [getStaticProps](https://nextjs.org/docs/basic-features/data-fetching/get-static-props) and [getStaticPaths](https://nextjs.org/docs/basic-features/data-fetching/get-static-paths.md). The end result is a Next.js application that uses one Apollo GraphQL schema to generate static pages at build time and also serve a GraphQL [API Route](https://nextjs.org/docs/api-routes/introduction) at runtime. ## Preview diff --git a/examples/api-routes-graphql/README.md b/examples/api-routes-graphql/README.md index 1c51cf4d7e38..fe53eb534cb4 100644 --- a/examples/api-routes-graphql/README.md +++ b/examples/api-routes-graphql/README.md @@ -1,6 +1,6 @@ # API routes with GraphQL server -Next.js ships with [API routes](https://github.com/vercel/next.js#api-routes), which provide an easy solution to build your own `API`. This example shows their usage alongside [apollo-server-micro](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-server-micro) to provide simple GraphQL server consumed by Next.js app. +Next.js ships with [API routes](https://nextjs.org/docs/api-routes/introduction), which provide an easy solution to build your own `API`. This example shows their usage alongside [apollo-server-micro](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-server-micro) to provide simple GraphQL server consumed by Next.js app. ## Preview diff --git a/examples/api-routes-rate-limit/package.json b/examples/api-routes-rate-limit/package.json index 568cc38e0b84..61b3034ed641 100644 --- a/examples/api-routes-rate-limit/package.json +++ b/examples/api-routes-rate-limit/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "lru-cache": "^6.0.0", - "next": "10.0.3", + "next": "latest", "react": "^17.0.2", "react-dom": "^17.0.2", "uuid": "^8.3.1" diff --git a/examples/api-routes/data.js b/examples/api-routes/data.js index ce2acde5041c..f57a9d0786fd 100644 --- a/examples/api-routes/data.js +++ b/examples/api-routes/data.js @@ -61,7 +61,7 @@ export const people = [ }, { id: '7', - name: 'Beru Whitesun lars', + name: 'Beru Whitesun Lars', height: '165', mass: '75', hair_color: 'brown', diff --git a/examples/api-routes/package.json b/examples/api-routes/package.json index 633936bee19f..56e1c8107ad3 100644 --- a/examples/api-routes/package.json +++ b/examples/api-routes/package.json @@ -9,6 +9,6 @@ "next": "latest", "react": "^17.0.2", "react-dom": "^17.0.2", - "swr": "0.1.18" + "swr": "^1.0.1" } } diff --git a/examples/auth-with-stytch/.env.template b/examples/auth-with-stytch/.env.template new file mode 100644 index 000000000000..4aed8e797e32 --- /dev/null +++ b/examples/auth-with-stytch/.env.template @@ -0,0 +1,8 @@ +# The two iron-session values may be left as-is while testing out this example app. +IRON_SESSION_COOKIE_NAME="stytch_next_example_cookie" +IRON_SESSION_PASSWORD="complex_password_at_least_32_characters_long" +# The below values may be found in your Stytch Dashboard: https://stytch.com/dashboard/api-keys +STYTCH_PROJECT_ENV=test +STYTCH_PROJECT_ID="YOUR_STYTCH_PROJECT_ID" +STYTCH_SECRET="YOUR_STYTCH_SECRET" +NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN="YOUR_STYTCH_PUBLIC_TOKEN" diff --git a/examples/with-prisma/.gitignore b/examples/auth-with-stytch/.gitignore similarity index 100% rename from examples/with-prisma/.gitignore rename to examples/auth-with-stytch/.gitignore diff --git a/examples/auth-with-stytch/README.md b/examples/auth-with-stytch/README.md new file mode 100644 index 000000000000..7d787948b523 --- /dev/null +++ b/examples/auth-with-stytch/README.md @@ -0,0 +1,68 @@ +# Stytch + Next.js example app on Vercel + +This is a [Stytch](https://stytch.com) + [Next.js](https://nextjs.org/) project that showcases how to enable elegant authentication in your applicaiton. + +

stytch

+ +In this repo, we have two sample auth flows: + +- SDK integration: This flow uses Stytch's React component to create a login and sign-up flow using [Email Magic Links](https://stytch.com/docs/api/send-by-email). +- API integration: This flow uses a custom UI with Stytch's backend API for [Onetime Passcodes(OTP) via SMS](https://stytch.com/docs/api/sms-otp-overview) authentication. + +Both flows use Stytch's [Node client library](https://github.com/stytchauth/stytch-node) and [`iron-session`](https://github.com/vvo/next-iron-session) for session management. + +**Note:** By default this example app enables three of our OAuth providers, Google, Microsoft, and Apple. If you haven't set up these OAuth providers in your [Dashboard](https://stytch.com/dashboard/oauth), you'll receive a redirect error when you attempt to login via those providers. You may remove all OAuth methods by removing `SDKProductTypes.oauth` from the `products` array in [pages/index.tsx](pages/index.tsx) or adjust which ones are displayed by via `oauthOptions.providers` in the same file. More detail on working with OAuth providers in our SDK may be found in our [Docs](https://stytch.com/docs/javascript-sdk#javascript-sdk/oauth). + +# Deploy on Vercel + +## Setting up Stytch + +The first step is to configure the appropriate redirect URLs for your project. You'll set these magic link redirect URLs in the [Redirect URLs](https://stytch.com/dashboard/redirect-urls) section of your Dashboard. Add `https://*.vercel.app:3000` as both a login and sign-up redirect URL. + +## Running the example app + +Now just click the deploy button below! Once you're signed in to your Vercel account, you'll be guided through how to get up and running quickly. Check out [.env.template](/.env.template) for pointers on filling in the appropriate environment variables for this step. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Fblob%2Fcanary%2Fexamples%2Fauth-with-stytch%2F&env=STYTCH_PROJECT_ENV,STYTCH_PROJECT_ID,STYTCH_SECRET,NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN,IRON_SESSION_PASSWORD,IRON_SESSION_COOKIE_NAME&envDescription=All%20variables%20here%20need%20values%2C%20see%20the%20following%20link%20for%20pointers%20on%20how%20to%20feel%20these%20out.&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Fblob%2Fcanary%2Fexamples%2Fauth-with-stytch%2F.env.template&project-name=stytch-nextjs-vercel&repo-name=stytch-nextjs-vercel&demo-title=Stytch%20on%20Next.js%20with%20Vercel&demo-description=Next.js%20example%20app%20using%20Stytch%20authentication&demo-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Fblob%2Fcanary%2Fexamples%2Fauth-with-stytch&demo-image=https%3A%2F%2Fstytch.com%2Flogo-preview.png) + +# Running locally via `vercel dev` + +## Setting up Stytch + +After signing up for Stytch, you'll need your Project's `project_id`, `secret`, and `public_token`. You can find these in the [API keys tab](https://stytch.com/dashboard/api-keys). + +Once you've gathered these values, add them to a new .env.local file. +Example: + +```bash +cp .env.template .env.local +# Replace your keys in new .env.local file +``` + +Next we'll configure the appropriate redirect URLs for your project, you'll set these magic link URLs for your project in the [Redirect URLs](https://stytch.com/dashboard/redirect-urls) section of your Dashboard. Add `http://localhost:3000/api/authenticate_magic_link` as both a login and sign-up redirect URL. + +## Running the example app + +Install dependencies by running + +```bash +npm install +# or +yarn install +``` + +You can then run a development server using: + +```bash +vercel dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +## Documentation + +Learn more about some of Stytch's products used in this example app: + +- [Stytch React](https://www.npmjs.com/package/@stytch/stytch-react) +- [Stytch's node client library](https://www.npmjs.com/package/stytch) +- [iron-session](https://github.com/vvo/next-iron-session) diff --git a/examples/auth-with-stytch/components/LoginEntryPoint.tsx b/examples/auth-with-stytch/components/LoginEntryPoint.tsx new file mode 100644 index 000000000000..a8bf3562dca2 --- /dev/null +++ b/examples/auth-with-stytch/components/LoginEntryPoint.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import styles from '../styles/Home.module.css' +import { LoginMethod } from '../lib/types' +import StytchContainer from './StytchContainer' + +type Props = { + setLoginMethod: (loginMethod: LoginMethod) => void +} + +const LoginEntryPoint = (props: Props) => { + const { setLoginMethod } = props + return ( + +

Hello Vercel!

+

+ This example app demonstrates how you can integrate with Stytch using + Next.js and deploy on Vercel. Now, let’s get started! +

+ + +
+ ) +} + +export default LoginEntryPoint diff --git a/examples/auth-with-stytch/components/LoginWithSMS.tsx b/examples/auth-with-stytch/components/LoginWithSMS.tsx new file mode 100644 index 000000000000..1c5e2fe229a8 --- /dev/null +++ b/examples/auth-with-stytch/components/LoginWithSMS.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react' +import SendOTPForm from './SendOTPForm' +import VerifyOTPForm from './VerifyOTPForm' +import StytchContainer from './StytchContainer' + +const LoginWithSMS = () => { + const [otpSent, setOTPSent] = useState(false) + const [phoneNumber, setPhoneNumber] = useState('') + const [methodId, setMethodId] = useState('') + + return ( + + {!otpSent ? ( + + ) : ( + + )} + + ) +} + +export default LoginWithSMS diff --git a/examples/auth-with-stytch/components/SendOTPForm.tsx b/examples/auth-with-stytch/components/SendOTPForm.tsx new file mode 100644 index 000000000000..b2276b076084 --- /dev/null +++ b/examples/auth-with-stytch/components/SendOTPForm.tsx @@ -0,0 +1,83 @@ +import React from 'react' +import { sendOTP } from '../lib/otpUtils' +import styles from '../styles/Home.module.css' + +type Props = { + phoneNumber: string + setMethodId: (methodId: string) => void + setOTPSent: (submitted: boolean) => void + setPhoneNumber: (phoneNumber: string) => void +} + +const SendOTPForm = (props: Props): JSX.Element => { + const { phoneNumber, setMethodId, setOTPSent, setPhoneNumber } = props + const [isDisabled, setIsDisabled] = React.useState(true) + + const isValidNumber = (phoneNumberValue: string) => { + // Regex validates phone numbers in (xxx)xxx-xxxx, xxx-xxx-xxxx, xxxxxxxxxx, and xxx.xxx.xxxx format + const regex = /^[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9]{4}$/g + if (phoneNumberValue.match(regex)) { + return true + } + return false + } + + const onPhoneNumberChange = (e: React.ChangeEvent<{ value: string }>) => { + setPhoneNumber(e.target.value) + if (isValidNumber(e.target.value)) { + setIsDisabled(false) + } else { + setIsDisabled(true) + } + } + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (isValidNumber(phoneNumber)) { + const methodId = await sendOTP(phoneNumber) + setMethodId(methodId) + setOTPSent(true) + } + } + + return ( +
+

Enter phone number

+

+ Enter your phone number to receive a passcode for authentication. +

+
+
+ + +
+

+ By continuing, you consent to receive an SMS for verification. Message + and data rates may apply. +

+ +
+
+ ) +} + +export default SendOTPForm diff --git a/examples/auth-with-stytch/components/StytchContainer.tsx b/examples/auth-with-stytch/components/StytchContainer.tsx new file mode 100644 index 000000000000..e0972d98f99a --- /dev/null +++ b/examples/auth-with-stytch/components/StytchContainer.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import styles from '../styles/Home.module.css' +import Image from 'next/image' +import lockup from '/public/powered-by-stytch.svg' + +type Props = { + children: React.ReactElement | React.ReactElement[] +} + +const StytchContainer = (props: Props) => { + const { children } = props + return ( +
+
{children}
+ Powered by Stytch +
+ ) +} + +export default StytchContainer diff --git a/examples/auth-with-stytch/components/VerifyOTPForm.tsx b/examples/auth-with-stytch/components/VerifyOTPForm.tsx new file mode 100644 index 000000000000..cf62eb5e73a8 --- /dev/null +++ b/examples/auth-with-stytch/components/VerifyOTPForm.tsx @@ -0,0 +1,168 @@ +import React from 'react' +import styles from '../styles/Home.module.css' +import { sendOTP } from '../lib/otpUtils' +import { useRouter } from 'next/router' + +// Handles auto-tabbing to next passcode digit input. +// Logic inspired from https://stackoverflow.com/questions/15595652/focus-next-input-once-reaching-maxlength-value. +const autoTab = (target: HTMLInputElement, key?: string) => { + if (target.value.length >= target.maxLength) { + let next = target + while ((next = next.nextElementSibling as HTMLInputElement)) { + if (next == null) break + if (next.tagName.toLowerCase() === 'input') { + next?.focus() + break + } + } + } + // Move to previous field if empty (user pressed backspace) + else if (target.value.length === 0) { + let previous = target + while ((previous = previous.previousElementSibling as HTMLInputElement)) { + if (previous == null) break + if (previous.tagName.toLowerCase() === 'input') { + previous.focus() + break + } + } + } +} + +type Props = { + methodId: string + phoneNumber: string +} + +const VerifyOTPForm = (props: Props) => { + const { methodId, phoneNumber } = props + const [isDisabled, setIsDisabled] = React.useState(true) + const [currentMethodId, setCurrentMethodId] = React.useState(methodId) + const [isError, setIsError] = React.useState(false) + const router = useRouter() + + const strippedNumber = phoneNumber.replace(/\D/g, '') + const parsedPhoneNumber = `(${strippedNumber.slice( + 0, + 3 + )}) ${strippedNumber.slice(3, 6)}-${strippedNumber.slice(6, 10)}` + + const isValidPasscode = () => { + const regex = /^[0-9]$/g + const inputs = document.getElementsByClassName(styles.passcodeInput) + for (let i = 0; i < inputs.length; i++) { + if (!(inputs[i] as HTMLInputElement).value.match(regex)) { + return false + } + } + return true + } + + const onPasscodeDigitChange = () => { + if (isValidPasscode()) { + setIsDisabled(false) + setIsError(false) + } else { + setIsDisabled(true) + } + } + + const resetPasscode = () => { + const inputs = document.getElementsByClassName(styles.passcodeInput) + for (let i = 0; i < inputs.length; i++) { + ;(inputs[i] as HTMLInputElement).value = '' + } + document.getElementById('digit-0')?.focus() + setIsDisabled(true) + } + + const resendCode = async () => { + const methodId = await sendOTP(phoneNumber) + setCurrentMethodId(methodId) + resetPasscode() + setIsError(false) + } + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (isValidPasscode()) { + let otpInput = '' + const inputs = document.getElementsByClassName(styles.passcodeInput) + for (let i = 0; i < inputs.length; i++) { + otpInput += (inputs[i] as HTMLInputElement).value + } + + const resp = await fetch('/api/authenticate_otp', { + method: 'POST', + body: JSON.stringify({ otpInput, methodId: currentMethodId }), + }) + + if (resp.status === 200) { + router.push('/profile') + } else { + setIsError(true) + resetPasscode() + } + } + } + + const renderPasscodeInputs = () => { + const inputs = [] + for (let i = 0; i < 6; i += 1) { + inputs.push( + autoTab(e.target as HTMLInputElement, e.key)} + placeholder="0" + size={1} + type="text" + /> + ) + } + return inputs + } + + return ( +
+

Enter passcode

+

+ A 6-digit passcode was sent to you at{' '} + {parsedPhoneNumber}. +

+
+
+

+ {isError ? 'Invalid code. Please try again.' : ''} +

+
+ {renderPasscodeInputs()} +
+
+
+

Didn’t get it?

+ +
+ +
+
+ ) +} + +export default VerifyOTPForm diff --git a/examples/auth-with-stytch/lib/loadStytch.ts b/examples/auth-with-stytch/lib/loadStytch.ts new file mode 100644 index 000000000000..2b3330527516 --- /dev/null +++ b/examples/auth-with-stytch/lib/loadStytch.ts @@ -0,0 +1,19 @@ +import * as stytch from 'stytch' + +let client: stytch.Client +const loadStytch = () => { + if (!client) { + client = new stytch.Client({ + project_id: process.env.STYTCH_PROJECT_ID || '', + secret: process.env.STYTCH_SECRET || '', + env: + process.env.STYTCH_PROJECT_ENV === 'live' + ? stytch.envs.live + : stytch.envs.test, + }) + } + + return client +} + +export default loadStytch diff --git a/examples/auth-with-stytch/lib/otpUtils.ts b/examples/auth-with-stytch/lib/otpUtils.ts new file mode 100644 index 000000000000..69a2bd10b91c --- /dev/null +++ b/examples/auth-with-stytch/lib/otpUtils.ts @@ -0,0 +1,11 @@ +export async function sendOTP(phoneNumber: string) { + const resp = await fetch('/api/send_otp', { + method: 'POST', + body: JSON.stringify({ + intlCode: '+1', + phoneNumber, + }), + }) + const data = await resp.json() + return data.methodId +} diff --git a/examples/auth-with-stytch/lib/types.ts b/examples/auth-with-stytch/lib/types.ts new file mode 100644 index 000000000000..d548d4c75fc2 --- /dev/null +++ b/examples/auth-with-stytch/lib/types.ts @@ -0,0 +1,4 @@ +export enum LoginMethod { + API, + SDK, +} diff --git a/examples/auth-with-stytch/lib/withSession.tsx b/examples/auth-with-stytch/lib/withSession.tsx new file mode 100644 index 000000000000..e00dba1ec30d --- /dev/null +++ b/examples/auth-with-stytch/lib/withSession.tsx @@ -0,0 +1,28 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import { Session, withIronSession } from 'next-iron-session' + +type NextIronRequest = NextApiRequest & { session: Session } + +type APIHandler = ( + req: NextIronRequest, + res: NextApiResponse +) => Promise + +export type ServerSideProps = ({ + req, +}: { + req: NextIronRequest +}) => Promise + +const withSession = (handler: APIHandler | ServerSideProps) => + withIronSession(handler, { + password: process.env.IRON_SESSION_PASSWORD || '', + cookieName: process.env.IRON_SESSION_COOKIE_NAME || '', + // if your localhost is served on http:// then disable the secure flag + cookieOptions: { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + }, + }) + +export default withSession diff --git a/examples/auth-with-stytch/next-env.d.ts b/examples/auth-with-stytch/next-env.d.ts new file mode 100644 index 000000000000..4f11a03dc6cc --- /dev/null +++ b/examples/auth-with-stytch/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/auth-with-stytch/package.json b/examples/auth-with-stytch/package.json new file mode 100644 index 000000000000..b5bc962e228d --- /dev/null +++ b/examples/auth-with-stytch/package.json @@ -0,0 +1,20 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@stytch/stytch-react": "^3.0.3", + "next": "12.0.7", + "next-iron-session": "^4.2.0", + "react": "17.0.2", + "react-dom": "17.0.2", + "stytch": "^3.6.1" + }, + "devDependencies": { + "@types/react": "17.0.37", + "typescript": "^4.5.4" + } +} diff --git a/examples/auth-with-stytch/pages/_app.tsx b/examples/auth-with-stytch/pages/_app.tsx new file mode 100644 index 000000000000..a8a5e0556e47 --- /dev/null +++ b/examples/auth-with-stytch/pages/_app.tsx @@ -0,0 +1,55 @@ +import '../styles/globals.css' +import styles from '../styles/Home.module.css' +import type { AppProps } from 'next/app' +import React from 'react' +import Head from 'next/head' +import Image from 'next/image' +import stytchLogo from '/public/stytch-logo.svg' +import vercelLogo from '/public/vercel-logotype-dark.svg' + +function MyApp({ Component, pageProps }: AppProps) { + return ( + + + + + Stytch + Next.js example app + +
+
+ + Stytch logo + +

+

+ + Vercel logo + +
+ +
+
+ +
+
+ ) +} +export default MyApp diff --git a/examples/auth-with-stytch/pages/_document.tsx b/examples/auth-with-stytch/pages/_document.tsx new file mode 100644 index 000000000000..83d9080a67d7 --- /dev/null +++ b/examples/auth-with-stytch/pages/_document.tsx @@ -0,0 +1,33 @@ +import Document, { + Html, + Head, + Main, + NextScript, + DocumentContext, +} from 'next/document' + +class MyDocument extends Document { + static async getInitialProps(ctx: DocumentContext) { + const initialProps = await Document.getInitialProps(ctx) + return { ...initialProps } + } + + render() { + return ( + + + + + +
+ + + + ) + } +} + +export default MyDocument diff --git a/examples/auth-with-stytch/pages/api/authenticate_magic_link.ts b/examples/auth-with-stytch/pages/api/authenticate_magic_link.ts new file mode 100644 index 000000000000..d0edf7fc568e --- /dev/null +++ b/examples/auth-with-stytch/pages/api/authenticate_magic_link.ts @@ -0,0 +1,39 @@ +// This API route authenticates a Stytch magic link. +import type { NextApiRequest, NextApiResponse } from 'next' +import { Session } from 'next-iron-session' +import withSession from '../../lib/withSession' +import loadStytch from '../../lib/loadStytch' +type NextIronRequest = NextApiRequest & { session: Session } + +type Data = { + errorString: string +} + +export async function handler( + req: NextIronRequest, + res: NextApiResponse +) { + if (req.method === 'GET') { + const client = loadStytch() + const { token } = req.query + try { + const resp = await client.magicLinks.authenticate(token as string) + // Set session + req.session.destroy() + req.session.set('user', { + id: resp.user_id, + }) + // Save additional user data here + await req.session.save() + res.redirect('/profile') + } catch (error) { + const errorString = JSON.stringify(error) + console.log(error) + res.status(400).json({ errorString }) + } + } else { + // Handle any other HTTP method + } +} + +export default withSession(handler) diff --git a/examples/auth-with-stytch/pages/api/authenticate_otp.ts b/examples/auth-with-stytch/pages/api/authenticate_otp.ts new file mode 100644 index 000000000000..71fe03622349 --- /dev/null +++ b/examples/auth-with-stytch/pages/api/authenticate_otp.ts @@ -0,0 +1,51 @@ +// This API route authenticates Stytch OTP codes. +import type { NextApiRequest, NextApiResponse } from 'next' +import { Session } from 'next-iron-session' +import withSession from '../../lib/withSession' +import loadStytch from '../../lib/loadStytch' +type NextIronRequest = NextApiRequest & { session: Session } + +type Data = { + msg: string +} + +export async function handler( + req: NextIronRequest, + res: NextApiResponse +) { + if (req.method === 'POST') { + const client = loadStytch() + const data = JSON.parse(req.body) + try { + // params are of type stytch.LoginOrCreateUserBySMSRequest + const params = { + code: data.otpInput, + method_id: data.methodId, + } + + const resp = await client.otps.authenticate(params) + if (resp.status_code.toString() === '200') { + // Set session + req.session.destroy() + // Save additional user data here + req.session.set('user', { + id: resp.user_id, + }) + await req.session.save() + res + .status(200) + .send({ msg: `successfully authenticated ${resp.user_id}` }) + } else { + throw Error('Error authenticating your code') + } + } catch (error) { + const errorString = JSON.stringify(error) + console.log(error) + res.status(400).json({ msg: errorString }) + } + } else { + // Handle any other HTTP method + } +} + +export default withSession(handler) diff --git a/examples/auth-with-stytch/pages/api/logout.ts b/examples/auth-with-stytch/pages/api/logout.ts new file mode 100644 index 000000000000..c0579d9bf077 --- /dev/null +++ b/examples/auth-with-stytch/pages/api/logout.ts @@ -0,0 +1,30 @@ +// This API route logs a user out. +import type { NextApiRequest, NextApiResponse } from 'next' +import { Session } from 'next-iron-session' +import withSession from '../../lib/withSession' +type NextIronRequest = NextApiRequest & { session: Session } + +type Data = { + errorString: string +} + +export async function handler( + req: NextIronRequest, + res: NextApiResponse +) { + if (req.method === 'POST') { + try { + // Set session + req.session.destroy() + res.redirect('/') + } catch (error) { + const errorString = JSON.stringify(error) + console.log(error) + res.status(400).json({ errorString }) + } + } else { + // Handle any other HTTP method + } +} + +export default withSession(handler) diff --git a/examples/auth-with-stytch/pages/api/send_otp.ts b/examples/auth-with-stytch/pages/api/send_otp.ts new file mode 100644 index 000000000000..06d31bd00932 --- /dev/null +++ b/examples/auth-with-stytch/pages/api/send_otp.ts @@ -0,0 +1,32 @@ +// This API route sends an OTP code to a specified number. +import type { NextApiRequest, NextApiResponse } from 'next' +import loadStytch from '../../lib/loadStytch' + +type Data = { + methodId: string +} + +export async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === 'POST') { + const client = loadStytch() + const data = JSON.parse(req.body) + try { + const phoneNumber = data.phoneNumber.replace(/\D/g, '') + + // params are of type stytch.LoginOrCreateUserBySMSRequest + const params = { + phone_number: `${data.intlCode}${phoneNumber}`, + } + + const resp = await client.otps.sms.loginOrCreate(params) + res.status(200).json({ methodId: resp.phone_id }) + } catch (error) { + console.log(error) + res.status(400) + } + } else { + // Handle any other HTTP method + } +} + +export default handler diff --git a/examples/auth-with-stytch/pages/index.tsx b/examples/auth-with-stytch/pages/index.tsx new file mode 100644 index 000000000000..588e6c806c86 --- /dev/null +++ b/examples/auth-with-stytch/pages/index.tsx @@ -0,0 +1,120 @@ +import React, { useEffect } from 'react' +import { useRouter } from 'next/router' +import { Stytch, StytchProps } from '@stytch/stytch-react' +import { OAuthProvidersTypes, SDKProductTypes } from '@stytch/stytch-js' +import styles from '../styles/Home.module.css' +import withSession, { ServerSideProps } from '../lib/withSession' +import LoginWithSMS from '../components/LoginWithSMS' +import { LoginMethod } from '../lib/types' +import LoginEntryPoint from '../components/LoginEntryPoint' + +// Set the URL base for redirect URLs. The three cases are as follows: +// 1. Running locally via `vercel dev`; VERCEL_URL will contain localhost, but will not be https. +// 2. Deploying via Vercel; VERCEL_URL will be generated on runtime and use https. +// 3. Running locally via `npm run dev`; VERCEL_URL will be undefined and the app will be at localhost. +let REDIRECT_URL_BASE = '' + +if (process.env.NEXT_PUBLIC_VERCEL_URL?.includes('localhost')) { + REDIRECT_URL_BASE = 'http://localhost:3000' +} else if (process.env.NEXT_PUBLIC_VERCEL_URL !== undefined) { + REDIRECT_URL_BASE = `https://${process.env.NEXT_PUBLIC_VERCEL_URL}` +} else { + REDIRECT_URL_BASE = 'http://localhost:3000' +} + +const stytchProps: StytchProps = { + loginOrSignupView: { + products: [SDKProductTypes.oauth, SDKProductTypes.emailMagicLinks], + emailMagicLinksOptions: { + loginRedirectURL: REDIRECT_URL_BASE + '/api/authenticate_magic_link', + loginExpirationMinutes: 30, + signupRedirectURL: REDIRECT_URL_BASE + '/api/authenticate_magic_link', + signupExpirationMinutes: 30, + createUserAsPending: false, + }, + oauthOptions: { + providers: [ + { type: OAuthProvidersTypes.Google }, + { type: OAuthProvidersTypes.Microsoft }, + { type: OAuthProvidersTypes.Apple }, + ], + }, + }, + style: { + fontFamily: '"Helvetica New", Helvetica, sans-serif', + primaryColor: '#0577CA', + primaryTextColor: '#090909', + width: '321px', + }, + publicToken: process.env.NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN || '', + callbacks: { + onEvent: (data) => { + // TODO: check whether the user exists in your DB + if (data.eventData.type === 'USER_EVENT_TYPE') { + console.log({ + userId: data.eventData.userId, + email: data.eventData.email, + }) + } + }, + onSuccess: (data) => console.log(data), + onError: (data) => console.log(data), + }, +} + +type Props = { + publicToken: string + user: { + id: string + } +} + +const App = (props: Props) => { + const { user, publicToken } = props + const [loginMethod, setLoginMethod] = React.useState(null) + const router = useRouter() + + useEffect(() => { + if (user) { + router.push('/profile') + } + }) + + const loginMethodMap: Record = { + [LoginMethod.API]: , + [LoginMethod.SDK]: ( +
+ +
+ ), + } + + return ( +
+ {loginMethod === null ? ( + + ) : ( + loginMethodMap[loginMethod] + )} +
+ ) +} + +const getServerSidePropsHandler: ServerSideProps = async ({ req }) => { + // Get the user's session based on the request + const user = req.session.get('user') ?? null + const props: Props = { + publicToken: stytchProps.publicToken, + user, + } + return { props } +} + +export const getServerSideProps = withSession(getServerSidePropsHandler) + +export default App diff --git a/examples/auth-with-stytch/pages/profile.tsx b/examples/auth-with-stytch/pages/profile.tsx new file mode 100644 index 000000000000..33896e29da93 --- /dev/null +++ b/examples/auth-with-stytch/pages/profile.tsx @@ -0,0 +1,61 @@ +import React, { useEffect } from 'react' +import styles from '../styles/Home.module.css' +import StytchContainer from '../components/StytchContainer' +import withSession, { ServerSideProps } from '../lib/withSession' +import { useRouter } from 'next/router' + +type Props = { + user?: { + id: string + } +} + +const Profile = (props: Props) => { + const { user } = props + const router = useRouter() + + useEffect(() => { + if (!user) { + router.replace('/') + } + }) + + const signOut = async () => { + const resp = await fetch('/api/logout', { method: 'POST' }) + if (resp.status === 200) { + router.push('/') + } + } + + return ( + <> + {!user ? ( +
+ ) : ( + +

{'Welcome!'}

+

+ Thank you for using Stytch! Here’s your user info. +

+
+            {JSON.stringify(user, null, 1).replace(' ', '')}
+          
+ +
+ )} + + ) +} + +const getServerSidePropsHandler: ServerSideProps = async ({ req }) => { + // Get the user's session based on the request + const user = req.session.get('user') ?? null + const props: Props = { user } + return { props } +} + +export const getServerSideProps = withSession(getServerSidePropsHandler) + +export default Profile diff --git a/examples/auth-with-stytch/public/example-app-image.png b/examples/auth-with-stytch/public/example-app-image.png new file mode 100644 index 000000000000..2ccfa04b11d9 Binary files /dev/null and b/examples/auth-with-stytch/public/example-app-image.png differ diff --git a/examples/auth-with-stytch/public/favicon.png b/examples/auth-with-stytch/public/favicon.png new file mode 100644 index 000000000000..63ccbc321282 Binary files /dev/null and b/examples/auth-with-stytch/public/favicon.png differ diff --git a/examples/auth-with-stytch/public/powered-by-stytch.svg b/examples/auth-with-stytch/public/powered-by-stytch.svg new file mode 100644 index 000000000000..09b0974a4185 --- /dev/null +++ b/examples/auth-with-stytch/public/powered-by-stytch.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/examples/auth-with-stytch/public/stars-and-stripes.png b/examples/auth-with-stytch/public/stars-and-stripes.png new file mode 100644 index 000000000000..08eb2fada9e0 Binary files /dev/null and b/examples/auth-with-stytch/public/stars-and-stripes.png differ diff --git a/examples/auth-with-stytch/public/stytch-logo.svg b/examples/auth-with-stytch/public/stytch-logo.svg new file mode 100644 index 000000000000..e07703278733 --- /dev/null +++ b/examples/auth-with-stytch/public/stytch-logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/examples/auth-with-stytch/public/vercel-logotype-dark.svg b/examples/auth-with-stytch/public/vercel-logotype-dark.svg new file mode 100644 index 000000000000..bb5a15a24c33 --- /dev/null +++ b/examples/auth-with-stytch/public/vercel-logotype-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/auth-with-stytch/styles/Home.module.css b/examples/auth-with-stytch/styles/Home.module.css new file mode 100644 index 000000000000..d30afe1a2503 --- /dev/null +++ b/examples/auth-with-stytch/styles/Home.module.css @@ -0,0 +1,194 @@ +.root { + background-color: #ecfaff; + width: 100%; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.container { + background-color: white; + border: 1px solid #ebebeb; + border-radius: 9px; + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.12); + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 80px; + width: 500px; + height: 500px; +} + +@media only screen and (max-width: 600px) { + .container { + border: none; + border-radius: 0; + box-shadow: none; + width: 100%; + } +} + +@media only screen and (max-width: 400px) { + .container { + padding: 40px; + } +} + +.primaryButton { + background-color: #19303d; +} + +.primaryButton:disabled { + background-color: #f3f5f6; + color: #5c727d; + opacity: 1; +} + +/* Nav bar styles */ + +.nav { + align-items: center; + background-color: #c7f1ff; + display: flex; + height: 64px; + justify-content: space-between; + padding: 0 28px; + position: absolute; + top: 0; + width: 100%; +} +.navLogos { + align-items: center; + background-color: #c7f1ff; + display: flex; + height: 64px; + padding: 0 28px; + top: 0; +} + +.navPlusSign { + padding: 0px 20px 10px 20px; + font-size: 30px; +} + +.docsNavItem { + cursor: pointer; + font-size: 20px; + font-weight: 500; + line-height: 30px; +} + +/* App entry styles */ + +.entrySubHeader { + margin: 32px 0; +} + +.entryButton { + background-color: #e5e8eb; + color: #19303d; + margin-bottom: 16px; +} + +/* SMS login styles */ + +.smsInstructions { + margin-top: 28px; +} + +.smsDisclaimer { + color: #5c727d; + font-size: 14px; + line-height: 20px; + margin-bottom: 16px; + margin-top: 24px; +} + +.telInput { + display: flex; + margin-top: 20px; + white-space: nowrap; +} + +.flag { + background: url('/stars-and-stripes.png') no-repeat scroll 8px 16px; + border-right: none; + border-radius: 3px 0 0 3px; + width: 80px; + padding-left: 48px; +} + +.phoneNumber { + border-left: none; + border-radius: 0 3px 3px 0; + padding-left: 0; + flex-grow: 1; + font-size: 18px; + width: calc(100%); +} + +.passcodeContainer { + margin-top: 4px; +} + +.passcodeInputContainer { + align-items: center; + display: flex; + justify-content: space-between; + width: 100%; +} + +.passcodeInput { + border-radius: 3px; + font-size: 20px; + width: 48px; + height: 45px; + text-align: center; +} + +.errorText { + color: red; + font-size: 14px; + height: 20px; + line-height: 20px; +} + +.resendCodeContainer { + margin: 28px 0; +} + +.resendCodeText { + color: #5c727d; + display: inline; + font-size: 16px; + line-height: 20px; +} + +.resendCodeButton { + background-color: white; + border: none; + cursor: pointer; + font-weight: 500; + height: fit-content; + padding: 0; + width: fit-content; +} + +/* Profile styles */ + +.profileSubHeader { + margin-bottom: 8px; + margin-top: 28px; +} + +.code { + background: #f3f5f6; + border-radius: 5px; + color: #10232e; + padding: 8px; + font-size: 16px; + font-family: IBM Plex Mono, monospace; + margin-bottom: 16px; + white-space: pre-wrap; +} diff --git a/examples/auth-with-stytch/styles/globals.css b/examples/auth-with-stytch/styles/globals.css new file mode 100644 index 000000000000..c2b16ddb2f93 --- /dev/null +++ b/examples/auth-with-stytch/styles/globals.css @@ -0,0 +1,73 @@ +html, +body { + padding: 0; + margin: 0; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; + font-family: IBM Plex Sans, 'sans-serif'; +} + +p { + color: #19303d; + font-size: 18px; + line-height: 25px; + margin: 0; +} + +strong { + color: black; + font-weight: 500; +} + +h2 { + font-size: 30px; + line-height: 40px; + margin: 0; +} + +input { + border: 1px solid #adbcc5; + box-sizing: border-box; + border-radius: 3px; + color: #19303d; + font-size: 18px; + height: 45px; + line-height: 25px; + padding: 10px; +} + +input:disabled { + background-color: white; + color: #19303d; +} + +input:focus { + outline: 0; +} + +input::placeholder { + color: #adbcc5; +} + +button, +input[type='submit'] { + border: none; + border-radius: 3px; + color: white; + font-size: 18px; + font-weight: 600; + height: 45px; + width: 100%; +} + +button:hover, +input[type='submit']:hover { + opacity: 0.8; +} diff --git a/examples/auth-with-stytch/tsconfig.json b/examples/auth-with-stytch/tsconfig.json new file mode 100644 index 000000000000..b8d597880a1a --- /dev/null +++ b/examples/auth-with-stytch/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/examples/auth0/pages/advanced/ssr-profile.js b/examples/auth0/pages/advanced/ssr-profile.js index 84d96d4dbee4..bbe88c59c751 100644 --- a/examples/auth0/pages/advanced/ssr-profile.js +++ b/examples/auth0/pages/advanced/ssr-profile.js @@ -21,7 +21,7 @@ export async function getServerSideProps({ req, res }) { // Here you can check authentication status directly before rendering the page, // however the page would be a serverless function, which is more expensive and // slower than a static page with client side authentication - const session = await auth0.getSession(req) + const session = await auth0.getSession(req, res) if (!session || !session.user) { res.writeHead(302, { diff --git a/examples/auth0/pages/api/callback.js b/examples/auth0/pages/api/callback.js index e06f9657d0d5..a1f9318dee34 100644 --- a/examples/auth0/pages/api/callback.js +++ b/examples/auth0/pages/api/callback.js @@ -2,7 +2,7 @@ import auth0 from '../../lib/auth0' export default async function callback(req, res) { try { - await auth0.handleCallback(req, res, { redirectTo: '/' }) + await auth0.handleCallback(req, res) } catch (error) { console.error(error) res.status(error.status || 500).end(error.message) diff --git a/examples/blog-starter-typescript/README.md b/examples/blog-starter-typescript/README.md index b74a6e5ad721..15856d06a494 100644 --- a/examples/blog-starter-typescript/README.md +++ b/examples/blog-starter-typescript/README.md @@ -36,6 +36,4 @@ Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&ut # Notes -This blog-starter-typescript uses [Tailwind CSS](https://tailwindcss.com). To control the generated stylesheet's filesize, this example uses Tailwind CSS' v2.0 [`purge` option](https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css) to remove unused CSS. - -[Tailwind CSS v2.0 no longer supports Node.js 8 or 10](https://tailwindcss.com/docs/upgrading-to-v2#upgrade-to-node-js-12-13-or-higher). To build your CSS you'll need to ensure you are running Node.js 12.13.0 or higher in both your local and CI environments. +`blog-starter-typescript` uses [Tailwind CSS](https://tailwindcss.com) [(v3.0)](https://tailwindcss.com/blog/tailwindcss-v3). diff --git a/examples/blog-starter-typescript/components/alert.tsx b/examples/blog-starter-typescript/components/alert.tsx index f804ff619286..067672f38082 100644 --- a/examples/blog-starter-typescript/components/alert.tsx +++ b/examples/blog-starter-typescript/components/alert.tsx @@ -10,8 +10,8 @@ const Alert = ({ preview }: Props) => { return (
@@ -21,7 +21,7 @@ const Alert = ({ preview }: Props) => { This page is a preview.{' '} Click here {' '} @@ -32,7 +32,7 @@ const Alert = ({ preview }: Props) => { The source code for this blog is{' '} available on GitHub diff --git a/examples/blog-starter-typescript/components/cover-image.tsx b/examples/blog-starter-typescript/components/cover-image.tsx index 6497d4c1bb85..78def8002412 100644 --- a/examples/blog-starter-typescript/components/cover-image.tsx +++ b/examples/blog-starter-typescript/components/cover-image.tsx @@ -12,8 +12,8 @@ const CoverImage = ({ title, src, slug }: Props) => { {`Cover ) diff --git a/examples/blog-starter-typescript/components/footer.tsx b/examples/blog-starter-typescript/components/footer.tsx index e8a7fdce4cd0..d2bcba33363b 100644 --- a/examples/blog-starter-typescript/components/footer.tsx +++ b/examples/blog-starter-typescript/components/footer.tsx @@ -3,10 +3,10 @@ import { EXAMPLE_PATH } from '../lib/constants' const Footer = () => { return ( -