Full Code of vercel/swr for AI

main 5fa29522f196 cached
344 files
663.2 KB
188.2k tokens
762 symbols
1 requests
Download .txt
Showing preview only (741K chars total). Download the full file or copy to clipboard to get everything.
Repository: vercel/swr
Branch: main
Commit: 5fa29522f196
Files: 344
Total size: 663.2 KB

Directory structure:
gitextract__gxvvg_b/

├── .codesandbox/
│   └── ci.json
├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── config.yml
│   ├── SECURITY.md
│   └── workflows/
│       ├── install/
│       │   └── action.yml
│       ├── test-canary.yml
│       ├── test-legacy-react.yml
│       ├── test-release.yml
│       └── trigger-release.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .npmrc
├── .swcrc
├── LICENSE
├── README.md
├── _internal/
│   └── package.json
├── e2e/
│   ├── site/
│   │   ├── README.md
│   │   ├── app/
│   │   │   ├── basic-ssr/
│   │   │   │   ├── block.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── concurrent-transition/
│   │   │   │   ├── page.tsx
│   │   │   │   └── transition-demo.tsx
│   │   │   ├── issue-2702/
│   │   │   │   ├── page.tsx
│   │   │   │   └── reproduction.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── mutate-server-action/
│   │   │   │   ├── action.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── page.tsx
│   │   │   ├── partially-hydrate/
│   │   │   │   ├── layout.tsx
│   │   │   │   ├── loading.tsx
│   │   │   │   ├── page.tsx
│   │   │   │   └── use-data.tsx
│   │   │   ├── perf/
│   │   │   │   └── page.tsx
│   │   │   ├── react-server-entry/
│   │   │   │   └── page.tsx
│   │   │   ├── render-count/
│   │   │   │   └── page.tsx
│   │   │   ├── render-preload-avoid-waterfall/
│   │   │   │   └── page.tsx
│   │   │   ├── render-preload-basic/
│   │   │   │   └── page.tsx
│   │   │   ├── render-promise-suspense-error/
│   │   │   │   └── page.tsx
│   │   │   ├── render-promise-suspense-resolve/
│   │   │   │   └── page.tsx
│   │   │   ├── render-promise-suspense-shared/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-avoid-rerender/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-cached-error/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-error/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-fallback/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-fetcher/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-infinite-preload/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-initial-data/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-keep-previous/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-key-change/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-multiple-fallbacks/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-no-revalidate/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-non-promise/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-null-key/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-same-data/
│   │   │   │   └── page.tsx
│   │   │   ├── server-prefetch-warning/
│   │   │   │   └── page.tsx
│   │   │   ├── suspense-after-preload/
│   │   │   │   ├── page.tsx
│   │   │   │   └── remote-data.tsx
│   │   │   ├── suspense-fallback/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── promise/
│   │   │   │       └── page.tsx
│   │   │   ├── suspense-infinite-get-key/
│   │   │   │   └── page.tsx
│   │   │   ├── suspense-retry/
│   │   │   │   ├── manual-retry.tsx
│   │   │   │   ├── page.tsx
│   │   │   │   └── use-remote-data.ts
│   │   │   └── suspense-undefined-key/
│   │   │       └── page.tsx
│   │   ├── component/
│   │   │   ├── manual-retry-mutate.tsx
│   │   │   ├── manual-retry.tsx
│   │   │   ├── only-render-in-client.tsx
│   │   │   └── use-remote-data.ts
│   │   ├── lib/
│   │   │   ├── sleep.ts
│   │   │   └── use-debug-history.ts
│   │   ├── next-env.d.ts
│   │   ├── next.config.js
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── api/
│   │   │   │   ├── data.ts
│   │   │   │   └── retry.ts
│   │   │   ├── suspense-retry-19.tsx
│   │   │   └── suspense-retry-mutate.tsx
│   │   └── tsconfig.json
│   └── test/
│       ├── concurrent-transition.test.ts
│       ├── initial-render.test.ts
│       ├── issue-2702-too-many-hooks.ts
│       ├── mutate-server-action.test.ts
│       ├── perf.test.ts
│       ├── preload-scenarios.test.ts
│       ├── promise-scenarios.test.ts
│       ├── server-prefetch-warning.test.ts
│       ├── stream-ssr.test.ts
│       ├── suspense-fallback.test.ts
│       ├── suspense-scenarios.test.ts
│       ├── suspense-undefined-key.test.ts
│       └── tsconfig.json
├── eslint.config.mjs
├── examples/
│   ├── .eslintrc
│   ├── api-hooks/
│   │   ├── README.md
│   │   ├── hooks/
│   │   │   ├── use-projects.js
│   │   │   └── use-repository.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── autocomplete-suggestions/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetcher.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── suggestions.js
│   │       └── index.js
│   ├── axios/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── useRequest.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── axios-typescript/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── useRequest.ts
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── [user]/
│   │   │   │   └── [repo].tsx
│   │   │   ├── api/
│   │   │   │   └── data.ts
│   │   │   └── index.tsx
│   │   └── tsconfig.json
│   ├── basic/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── basic-typescript/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.ts
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── [user]/
│   │   │   │   └── [repo].tsx
│   │   │   ├── api/
│   │   │   │   └── data.ts
│   │   │   └── index.tsx
│   │   └── tsconfig.json
│   ├── focus-revalidate/
│   │   ├── README.md
│   │   ├── components/
│   │   │   └── button.js
│   │   ├── libs/
│   │   │   ├── auth.js
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── user.js
│   │       └── index.js
│   ├── global-fetcher/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── _app.js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── infinite/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── infinite-scroll/
│   │   ├── README.md
│   │   ├── hooks/
│   │   │   └── useOnScreen.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── local-state-sharing/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── store.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── optimistic-ui/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── _app.js
│   │   │   ├── api/
│   │   │   │   └── todos.js
│   │   │   └── index.js
│   │   └── styles.css
│   ├── optimistic-ui-immer/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── prefetch-preload/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── refetch-interval/
│   │   ├── README.md
│   │   ├── components/
│   │   │   └── button.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── server-render/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetcher.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [pokemon].js
│   │       └── index.js
│   ├── storage-tab-sync/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── storage.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── subscription/
│   │   ├── README.md
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── suspense/
│   │   ├── README.md
│   │   ├── app/
│   │   │   ├── layout.jsx
│   │   │   └── rsc/
│   │   │       ├── [user]/
│   │   │       │   └── [repo]/
│   │   │       │       ├── error.jsx
│   │   │       │       ├── loading.jsx
│   │   │       │       ├── page.jsx
│   │   │       │       └── repo.jsx
│   │   │       ├── loading.jsx
│   │   │       ├── page.jsx
│   │   │       └── repos.jsx
│   │   ├── components/
│   │   │   └── error-handling.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── suspense-global/
│   │   ├── README.md
│   │   ├── components/
│   │   │   └── error-handling.ts
│   │   ├── global-swr-config.tsx
│   │   ├── libs/
│   │   │   └── fetch.ts
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── [user]/
│   │   │   │   ├── [repo].tsx
│   │   │   │   └── detail.tsx
│   │   │   ├── _app.tsx
│   │   │   ├── api/
│   │   │   │   └── data.ts
│   │   │   ├── index.tsx
│   │   │   └── repos.tsx
│   │   └── tsconfig.json
│   └── suspense-retry/
│       ├── app/
│       │   ├── api/
│       │   │   └── route.ts
│       │   ├── layout.tsx
│       │   ├── manual-retry.tsx
│       │   ├── page.tsx
│       │   └── use-remote-data.ts
│       ├── next-env.d.ts
│       ├── next.config.js
│       ├── package.json
│       ├── pages/
│       │   └── retry.tsx
│       └── tsconfig.json
├── immutable/
│   └── package.json
├── infinite/
│   └── package.json
├── jest.config.build.js
├── jest.config.js
├── mutation/
│   └── package.json
├── package.json
├── playwright.config.js
├── pnpm-workspace.yaml
├── scripts/
│   └── bump-next-version.js
├── src/
│   ├── _internal/
│   │   ├── constants.ts
│   │   ├── events.ts
│   │   ├── index.react-server.ts
│   │   ├── index.ts
│   │   ├── types.ts
│   │   └── utils/
│   │       ├── cache.ts
│   │       ├── config-context.ts
│   │       ├── config.ts
│   │       ├── devtools.ts
│   │       ├── env.ts
│   │       ├── global-state.ts
│   │       ├── hash.ts
│   │       ├── helper.ts
│   │       ├── merge-config.ts
│   │       ├── middleware-preset.ts
│   │       ├── mutate.ts
│   │       ├── normalize-args.ts
│   │       ├── preload.ts
│   │       ├── resolve-args.ts
│   │       ├── serialize.ts
│   │       ├── shared.ts
│   │       ├── subscribe-key.ts
│   │       ├── timestamp.ts
│   │       ├── use-swr-config.ts
│   │       ├── web-preset.ts
│   │       └── with-middleware.ts
│   ├── immutable/
│   │   └── index.ts
│   ├── index/
│   │   ├── config.ts
│   │   ├── index.react-server.ts
│   │   ├── index.ts
│   │   ├── serialize.ts
│   │   └── use-swr.ts
│   ├── infinite/
│   │   ├── index.react-server.ts
│   │   ├── index.ts
│   │   ├── serialize.ts
│   │   └── types.ts
│   ├── mutation/
│   │   ├── index.ts
│   │   ├── state.ts
│   │   └── types.ts
│   └── subscription/
│       ├── index.ts
│       └── types.ts
├── subscription/
│   └── package.json
├── test/
│   ├── jest-setup.ts
│   ├── tsconfig.json
│   ├── type/
│   │   ├── .eslintrc
│   │   ├── config.tsx
│   │   ├── fetcher.ts
│   │   ├── helper-types.tsx
│   │   ├── internal.tsx
│   │   ├── mutate.ts
│   │   ├── mutation.ts
│   │   ├── option-fetcher.ts
│   │   ├── preload.ts
│   │   ├── subscription.ts
│   │   ├── suspense/
│   │   │   ├── helper-types.tsx
│   │   │   ├── suspense.ts
│   │   │   └── tsconfig.json
│   │   ├── trigger.ts
│   │   ├── tsconfig.json
│   │   └── utils.ts
│   ├── unit/
│   │   ├── serialize.test.ts
│   │   ├── utils.test.tsx
│   │   └── web-preset.test.ts
│   ├── use-swr-cache.test.tsx
│   ├── use-swr-concurrent-rendering.test.tsx
│   ├── use-swr-config-callbacks.test.tsx
│   ├── use-swr-config.test.tsx
│   ├── use-swr-context-config.test.tsx
│   ├── use-swr-devtools.test.tsx
│   ├── use-swr-error.test.tsx
│   ├── use-swr-fetcher.test.tsx
│   ├── use-swr-focus.test.tsx
│   ├── use-swr-immutable.test.tsx
│   ├── use-swr-infinite-preload.test.tsx
│   ├── use-swr-infinite.test.tsx
│   ├── use-swr-integration.test.tsx
│   ├── use-swr-key.test.tsx
│   ├── use-swr-laggy.test.tsx
│   ├── use-swr-legacy-react.test.tsx
│   ├── use-swr-loading.test.tsx
│   ├── use-swr-local-mutation.test.tsx
│   ├── use-swr-middlewares.test.tsx
│   ├── use-swr-node-env.test.tsx
│   ├── use-swr-offline.test.tsx
│   ├── use-swr-preload.test.tsx
│   ├── use-swr-promise.test.tsx
│   ├── use-swr-reconnect.test.tsx
│   ├── use-swr-refresh.test.tsx
│   ├── use-swr-remote-mutation.test.tsx
│   ├── use-swr-revalidate.test.tsx
│   ├── use-swr-server.test.tsx
│   ├── use-swr-streaming-ssr.test.tsx
│   ├── use-swr-subscription.test.tsx
│   ├── use-swr-suspense.test.tsx
│   └── utils.tsx
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .codesandbox/ci.json
================================================
{
  "sandboxes": ["swr-basic-p7dg6", "swr-states-4une7", "swr-infinite-jb5bm", "swr-ssr-j9b2y"],
  "node": "18",
  "installCommand": "csb:install",
  "buildCommand": "csb:build"
}


================================================
FILE: .editorconfig
================================================
root = true

[*.{js,ts,jsx,tsx}]
indent_size = 2
indent_style = space

================================================
FILE: .github/CODEOWNERS
================================================
*    @shuding @huozhi


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
 advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as physical or electronic
 address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
 professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at coc@vercel.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality concerning the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq


================================================
FILE: .github/CONTRIBUTING.md
================================================
# SWR Contribution Guidelines

Thank you for reading this guide and we appreciate any contribution.

## Ask a Question

You can use the repository's [Discussions](https://github.com/vercel/swr/discussions) page to ask any questions, post feedback, or share your experience on how you use this library.

## Report a Bug

Whenever you find something which is not working properly, please first search the repository's [Issues](https://github.com/vercel/swr/issues) page and make sure it's not reported by someone else already.

If not, feel free to open an issue with a detailed description of the problem and the expected behavior. And reproduction (for example a [CodeSandbox](https://codesandbox.io) link) will be extremely helpful.

## Request for a New Feature

For new features, it would be great to have some discussions from the community before starting working on it. You can either create an issue (if there isn't one) or post a thread on the [Discussions](https://github.com/vercel/swr/discussions) page to describe the feature that you want to have.

If possible, you can add another additional context like how this feature can be implemented technically, what other alternative solutions we can have, etc.

## Open a PR for Bugfix or Feature

### Local Development with Examples

To run SWR locally, you can start it with any example in the `examples` folder. You need to set up the example and run the command in the root directory for overriding SWR and its dependencies to local assets.

First of all, build SWR assets

```sh
corepack enable
corepack pnpm install

pnpm watch
```

Install dependency of the target example, for instance `examples/basic`:


```sh
# by default it will run next dev for the example
pnpm next dev examples/basic
```

All examples are built with Next.js, so Next.js commands are all supported:

```sh
# if you want to build and start
pnpm next build examples/basic
pnpm next start examples/basic
```
## Update Documentation

To update the [SWR Documentation](https://swr.vercel.app), you can contribute to the [website repository](https://github.com/vercel/swr-site).


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a bug report for the SWR library
---

# Bug report

## Description / Observed Behavior

What kind of issues did you encounter with SWR?

## Expected Behavior

How did you expect SWR to behave here?

## Repro Steps / Code Example

Or share your code snippet or a [CodeSandbox](https://codesandbox.io) link is also appreciated!

## Additional Context

SWR version. 
Add any other context about the problem here.



================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Ask Question
    url: https://github.com/vercel/swr/discussions
    about: Ask questions and discuss with other community members


================================================
FILE: .github/SECURITY.md
================================================
# Reporting Security Issues

If you believe you have found a security vulnerability in SWR, 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


================================================
FILE: .github/workflows/install/action.yml
================================================
name: 'Install'
description: 'Set up and install dependencies'
runs:
  using: composite
  steps:
    - name: Setup pnpm
      uses: pnpm/action-setup@v4

    - name: Lock Corepack version
      shell: bash
      run: pnpm i -g corepack@0.31.0

    - name: Use Node.js lts
      uses: actions/setup-node@v4
      with:
        node-version: 22
        cache: pnpm
        registry-url: 'https://registry.npmjs.org'

    - name: Install Dependencies
      shell: bash
      run: |
        corepack enable
        node -v
        pnpm -v
        pnpm install


================================================
FILE: .github/workflows/test-canary.yml
================================================
name: Test React Canary

on:
  workflow_dispatch:
  schedule:
    - cron: '0 0 * * *'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install
        uses: ./.github/workflows/install

      - name: Install Canary
        run: corepack pnpm upgrade react@canary react-dom@canary use-sync-external-store@canary

      - name: Lint and test
        env:
          TEST_REACT_CANARY: 1
        run: |
          pnpm clean
          pnpm build
          pnpm test
          pnpm test:build
          pnpm test-typing


================================================
FILE: .github/workflows/test-legacy-react.yml
================================================
name: Test React 17

on:
  push:
    branches:
      - main
    tags:
      - v*
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install
        uses: ./.github/workflows/install

      - name: Test
        env:
          TEST_REACT_LEGACY: 1
        run: |
          pnpm clean
          pnpm build
          pnpm test
          pnpm test:build
          pnpm test-typing


================================================
FILE: .github/workflows/test-release.yml
================================================
name: Test and Release

on:
  push:
    branches:
      - main
    tags:
      - v*
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install
        uses: ./.github/workflows/install

      - name: Lint and test
        run: |
          pnpm clean
          pnpm build
          pnpm run-all-checks
          npm pack
          pnpm attw
          pnpm test
          pnpm test:build
          pnpm test-typing
  e2e:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v1.57.0-noble
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install
        uses: ./.github/workflows/install

      - name: E2E Tests
        run: |
          pnpm clean
          pnpm build
          pnpm build:e2e
          pnpm test:e2e
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report
  release:
    runs-on: ubuntu-latest
    needs: ["test", "e2e"]
    if: startsWith(github.ref, 'refs/tags/v')
    permissions:
      id-token: write
    steps:
      - name: Check out
        uses: actions/checkout@v4

      - name: Install
        uses: ./.github/workflows/install

      - name: Determine tag
        id: determine_tag
        run: |
          echo "tag=$(echo $GITHUB_REF | grep -Eo 'alpha|beta|canary|rc')" >> $GITHUB_OUTPUT

      - name: Publish to versioned tag
        if: steps.determine_tag.outputs.tag != ''
        run: |
          echo "Publishing to ${{ steps.determine_tag.outputs.tag }} tag"
          npm publish --access public --no-git-checks --provenance --tag ${{ steps.determine_tag.outputs.tag }}
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}

      - name: Publish to latest
        if: steps.determine_tag.outputs.tag == ''
        run: |
          echo "Publishing to latest"
          npm publish --access public --no-git-checks --provenance
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}


================================================
FILE: .github/workflows/trigger-release.yml
================================================
on:
  workflow_dispatch:
    inputs:
      releaseType:
        description: Release stable or beta?
        required: true
        type: choice
        options:
          - beta
          - stable

      semverType:
        description: semver type?
        type: choice
        options:
          - patch
          - minor
          - major

name: Trigger Release

env:
  SEMVER_TYPE: ${{ github.event.inputs.semverType }}
  RELEASE_TYPE: ${{ github.event.inputs.releaseType }}

jobs:
  start:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 10
          token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
      - name: Install
        uses: ./.github/workflows/install
      - name: Test
        run: |
          pnpm clean
          pnpm build
          pnpm run-all-checks
          pnpm test:build

      - name: Configure git
        run: |
          git config user.name "vercel-release-bot"
          git config user.email "infra+release@vercel.com"

      - name: Bump version and tag
        run: |
          node ./scripts/bump-next-version.js

      - name: Git push
        run: |
          git push origin main
          git push origin --tags


================================================
FILE: .gitignore
================================================
node_modules
dist
*.log
*.tgz
.env
.next
.DS_Store
.idea
.vscode
.eslintcache
examples/**/yarn.lock
package-lock.json
*.tsbuildinfo
coverage
.rollup.cache
playwright-report
test-results

================================================
FILE: .husky/pre-commit
================================================
pnpm lint-staged && pnpm types:check


================================================
FILE: .npmrc
================================================
# prevent sub-packages from installing peer-deps (multiple react versions)
auto-install-peers=false

================================================
FILE: .swcrc
================================================
{
  "$schema": "https://json.schemastore.org/swcrc",
  "sourceMaps": true,
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true
    },
    "transform": {
      "react": {
        "runtime": "automatic"
      }
    }
  }
}


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2023 Vercel, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
[![SWR](https://assets.vercel.com/image/upload/v1572289618/swr/banner.png)](https://swr.vercel.app)

<p align="center">
  <a aria-label="Vercel logo" href="https://vercel.com">
    <img src="https://badgen.net/badge/icon/Made%20by%20Vercel?icon=zeit&label&color=black&labelColor=black">
  </a>
  <br/>
  <a aria-label="NPM version" href="https://www.npmjs.com/package/swr">
    <img alt="" src="https://badgen.net/npm/v/swr">
  </a>
  <a aria-label="Package size" href="https://bundlephobia.com/result?p=swr">
    <img alt="" src="https://badgen.net/bundlephobia/minzip/swr">
  </a>
  <a aria-label="License" href="https://github.com/vercel/swr/blob/main/LICENSE">
    <img alt="" src="https://badgen.net/npm/license/swr">
  </a>
</p>

## Introduction

SWR is a React Hooks library for data fetching.

The name “**SWR**” is derived from `stale-while-revalidate`, a cache invalidation strategy popularized by [HTTP RFC 5861](https://tools.ietf.org/html/rfc5861).
**SWR** first returns the data from cache (stale), then sends the request (revalidate), and finally comes with the up-to-date data again.

With just one hook, you can significantly simplify the data fetching logic in your project. And it also covered in all aspects of speed, correctness, and stability to help you build better experiences:

- **Fast**, **lightweight** and **reusable** data fetching
- Transport and protocol agnostic
- Built-in **cache** and request deduplication
- **Real-time** experience
- Revalidation on focus
- Revalidation on network recovery
- Polling
- Pagination and scroll position recovery
- SSR and SSG
- Local mutation (Optimistic UI)
- Built-in smart error retry
- TypeScript
- React Suspense
- React Native

...and a lot more.

With SWR, components will get **a stream of data updates constantly and automatically**. Thus, the UI will be always **fast** and **reactive**.

---

**View full documentation and examples on [swr.vercel.app](https://swr.vercel.app).**

<br/>

## Quick Start

```js
import useSWR from 'swr'

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}
```

In this example, the React Hook `useSWR` accepts a `key` and a `fetcher` function.
The `key` is a unique identifier of the request, normally the URL of the API. And the `fetcher` accepts
`key` as its parameter and returns the data asynchronously.

`useSWR` also returns 3 values: `data`, `isLoading` and `error`. When the request (fetcher) is not yet finished,
`data` will be `undefined` and `isLoading` will be `true`. When we get a response, it sets `data` and `error` based on the result
of `fetcher`, `isLoading` to false and rerenders the component.

Note that `fetcher` can be any asynchronous function, you can use your favourite data-fetching
library to handle that part.

---

**View full documentation and examples on [swr.vercel.app](https://swr.vercel.app).**

<br/>

## Authors

This library is created by the team behind [Next.js](https://nextjs.org), with contributions from our community:

- Shu Ding ([@shuding\_](https://x.com/shuding_)) - [Vercel](https://vercel.com)
- Jiachi Liu ([@huozhi](https://x.com/huozhi)) - [Vercel](https://vercel.com)
- Guillermo Rauch ([@rauchg](https://x.com/rauchg)) - [Vercel](https://vercel.com)
- Joe Haddad ([@timer150](https://x.com/timer150)) - [Vercel](https://vercel.com)
- Paco Coursey ([@pacocoursey](https://x.com/pacocoursey)) - [Vercel](https://vercel.com)

[Contributors](https://github.com/vercel/swr/graphs/contributors)

Thanks to Ryan Chen for providing the awesome `swr` npm package name!

<br/>

## License

The MIT License.


================================================
FILE: _internal/package.json
================================================
{
  "main": "../dist/_internal/index.js",
  "module": "../dist/_internal/index.mjs",
  "types": "../dist/_internal/index.d.ts",
  "private": true
}


================================================
FILE: e2e/site/README.md
================================================
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.


================================================
FILE: e2e/site/app/basic-ssr/block.tsx
================================================
'use client'

import useSWR from 'swr'
import { useDebugHistory } from '~/lib/use-debug-history'

export default function Block() {
  const { data } = useSWR<string>('/api/data', async (url: string) => {
    const res = await fetch(url).then(v => v.json())
    return res.name
  })
  const debugRef = useDebugHistory(data, 'history:')
  return (
    <>
      <div ref={debugRef}></div>
      <div>result:{data || 'undefined'}</div>
    </>
  )
}


================================================
FILE: e2e/site/app/basic-ssr/page.tsx
================================================
import Block from './block'

export default function BasicSSRPage() {
  return <Block />
}


================================================
FILE: e2e/site/app/concurrent-transition/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const TransitionDemo = dynamic(() => import('./transition-demo'), {
  ssr: false
})

export default function ConcurrentTransitionPage() {
  return (
    <div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
      <h1>React 19 Concurrent Transition Test</h1>
      <p>
        This page tests SWR&apos;s behavior with React 19 concurrent
        transitions. When using useTransition, SWR should &quot;pause&quot;
        loading states to provide smooth UX.
      </p>
      <Suspense fallback={<div>Loading page...</div>}>
        <TransitionDemo />
      </Suspense>
    </div>
  )
}


================================================
FILE: e2e/site/app/concurrent-transition/transition-demo.tsx
================================================
'use client'

import React, { useState, useTransition, Suspense, useCallback } from 'react'
import useSWR from 'swr'

// Simulate API data fetching with delay
const fetcher = async (key: string): Promise<string> => {
  // Slightly longer delay to make transition behavior more observable
  await new Promise(resolve => setTimeout(resolve, 150))
  return key
}

// Component that uses SWR with suspense
function DataComponent({ swrKey }: { swrKey: string }) {
  const { data } = useSWR(swrKey, fetcher, {
    dedupingInterval: 0,
    suspense: true,
    // React 19 improvements for concurrent features
    keepPreviousData: false
  })

  return <span data-testid="data-content">data:{data}</span>
}

export default function TransitionDemo() {
  const [isPending, startTransition] = useTransition()
  const [key, setKey] = useState('initial-key')

  const handleTransition = useCallback(() => {
    startTransition(() => {
      setKey('new-key')
    })
  }, [])

  return (
    <div>
      <h2>React 19 Concurrent Transition Demo</h2>
      <div
        onClick={handleTransition}
        data-testid="transition-trigger"
        style={{
          cursor: 'pointer',
          padding: '20px',
          border: '1px solid #ccc',
          borderRadius: '4px',
          backgroundColor: isPending ? '#f0f0f0' : '#fff'
        }}
      >
        <div data-testid="pending-state">isPending:{isPending ? '1' : '0'}</div>
        <Suspense
          fallback={<span data-testid="loading-fallback">loading</span>}
        >
          <DataComponent swrKey={key} />
        </Suspense>
        <p style={{ fontSize: '12px', color: '#666', marginTop: '10px' }}>
          Click to test concurrent transition behavior
        </p>
      </div>
    </div>
  )
}


================================================
FILE: e2e/site/app/issue-2702/page.tsx
================================================
import Comp from './reproduction'

export default function Page() {
  return (
    <div>
      <Comp></Comp>
    </div>
  )
}


================================================
FILE: e2e/site/app/issue-2702/reproduction.tsx
================================================
'use client'
import useSWR, { preload } from 'swr'
import { Suspense, use, useEffect, useState } from 'react'

const sleep = (time: number, data: string) =>
  new Promise<string>(resolve => {
    setTimeout(() => resolve(data), time)
  })

const Bug = () => {
  const a = use(preload('a', () => sleep(1000, 'a')))
  const { data: b } = useSWR('b', () => sleep(2000, 'b'), {
    suspense: true
  })
  useState(b)
  return (
    <div>
      {a},{b}
    </div>
  )
}

const Comp = () => {
  const [loading, setLoading] = useState(true)

  // To prevent SSR
  useEffect(() => {
    setLoading(false)
  }, [])

  if (loading) {
    return <span>Loading...</span>
  }
  return (
    <Suspense fallback={<div>fetching</div>}>
      <Bug></Bug>
    </Suspense>
  )
}

export default Comp


================================================
FILE: e2e/site/app/layout.tsx
================================================
export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      {/*
        <head /> will contain the components returned by the nearest parent
        head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
      */}
      <head />
      <body>{children}</body>
    </html>
  )
}


================================================
FILE: e2e/site/app/mutate-server-action/action.tsx
================================================
'use server'

export async function action(): Promise<{ result: number }> {
  await sleep(500)
  return { result: 10086 }
}

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, ms)
  })
}


================================================
FILE: e2e/site/app/mutate-server-action/page.tsx
================================================
'use client'
import useSWRMutation from 'swr/mutation'
import { action } from './action'

const useServerActionMutation = () =>
  useSWRMutation('/api/mutate-server-action', () => action())

const Page = () => {
  const { trigger, data, isMutating } = useServerActionMutation()
  return (
    <div>
      <button onClick={() => trigger()}>mutate</button>
      <div>isMutating: {isMutating.toString()}</div>
      <div>data: {data?.result}</div>
    </div>
  )
}

export default Page


================================================
FILE: e2e/site/app/page.tsx
================================================
'use client'

import { Suspense, useReducer } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'

const fetcher = async (key: string) => {
  // Add a small delay to simulate network request
  await new Promise(resolve => setTimeout(resolve, 100))
  return 'SWR'
}

const Section = ({ trigger }: { trigger: boolean }) => {
  const { data } = useSWR(trigger ? 'test-key' : undefined, fetcher, {
    suspense: true
  })
  return <div>{data || 'empty'}</div>
}

export default function Page() {
  const [trigger, toggle] = useReducer(x => !x, false)

  return (
    <OnlyRenderInClient>
      <button onClick={toggle}>toggle</button>
      <Suspense fallback={<div>fallback</div>}>
        <Section trigger={trigger} />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/partially-hydrate/layout.tsx
================================================
'use client'
import type { PropsWithChildren } from 'react'
import { useDebugHistory } from '~/lib/use-debug-history'
import useData from './use-data'

export default function Layout({ children }: PropsWithChildren) {
  const { data } = useData()
  const debugRef = useDebugHistory(data, 'first history:')
  return (
    <html>
      <head />
      <body>
        <div>
          <div ref={debugRef}></div>
          <>first data:{data || 'undefined'}</>
        </div>
        {children}
      </body>
    </html>
  )
}


================================================
FILE: e2e/site/app/partially-hydrate/loading.tsx
================================================
export default function Loading() {
  return <div>Loading...</div>
}


================================================
FILE: e2e/site/app/partially-hydrate/page.tsx
================================================
'use client'
import { useDebugHistory } from '~/lib/use-debug-history'
import useData from './use-data'
import { use } from 'react'

let resolved = false
const susp = new Promise(res => {
  setTimeout(() => {
    resolved = true
    res(true)
  }, 2000)
})

export default function Page() {
  // We trigger the suspense boundary here!
  if (!resolved) {
    use(susp)
  }

  const { data } = useData()
  const debugRef = useDebugHistory(data, 'second history:')
  return (
    <div>
      <div ref={debugRef}></div>
      <>second data (delayed hydration):{data || 'undefined'}</>
    </div>
  )
}


================================================
FILE: e2e/site/app/partially-hydrate/use-data.tsx
================================================
import useSWR from 'swr'

export default function useData() {
  return useSWR<string>('/api/data', async (url: string) => {
    const res = await fetch(url).then(v => v.json())
    return res.name
  })
}


================================================
FILE: e2e/site/app/perf/page.tsx
================================================
'use client'
import { useState } from 'react'
import useSWR from 'swr'

const elementCount = 10_000
const useData = () => {
  return useSWR('1', async (url: string) => {
    return 1
  })
}

const HookUser = () => {
  const { data } = useData()
  return <div>{data}</div>
}
/**
 * This renders 10,000 divs and is used to compare against the render performance
 * when using swr.
 */
const CheapComponent = () => {
  const cheapComponents = Array.from({ length: elementCount }, (_, i) => (
    <div key={i}>{i}</div>
  ))
  return (
    <div>
      <h2>Cheap Component</h2>
      {cheapComponents}
    </div>
  )
}

/**
 * This renders 10,000 divs, each of which uses the same swr hook.
 */
const ExpensiveComponent = () => {
  const hookComponents = Array.from({ length: elementCount }, (_, i) => (
    <HookUser key={i} />
  ))
  return (
    <div>
      <h2>Expensive Component</h2>
      {hookComponents}
    </div>
  )
}

export default function PerformancePage() {
  const [renderExpensive, setRenderExpensive] = useState(false)
  return (
    <div>
      <h1>Performance Page</h1>
      <label>
        <input
          type="checkbox"
          checked={renderExpensive}
          onChange={e => setRenderExpensive(e.target.checked)}
        />
        Render with swr
      </label>
      {!renderExpensive ? <CheapComponent /> : <ExpensiveComponent />}
    </div>
  )
}


================================================
FILE: e2e/site/app/react-server-entry/page.tsx
================================================
import { unstable_serialize } from 'swr'
import { unstable_serialize as infinite_unstable_serialize } from 'swr/infinite'

export default function Page() {
  return (
    <>
      <div>SWR Server Component entry test</div>
      <div>unstable_serialize: {unstable_serialize('useSWR')}</div>
      <div>
        infinite_unstable_serialize:{' '}
        {infinite_unstable_serialize(() => 'useSWRInfinite')}
      </div>
    </>
  )
}


================================================
FILE: e2e/site/app/render-count/page.tsx
================================================
'use client'
import useSWR from 'swr'

export default function Page() {
  useSWR('swr should not cause extra rerenders')
  console.count('render')
  return <div>render count pages</div>
}


================================================
FILE: e2e/site/app/render-preload-avoid-waterfall/page.tsx
================================================
'use client'

import { Suspense, useEffect, useState } from 'react'
import useSWR, { preload } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

const keyA = 'render-preload-avoid-waterfall:a'
const keyB = 'render-preload-avoid-waterfall:b'
const delay = 200

async function fetcherA() {
  await sleep(delay)
  return 'foo'
}

async function fetcherB() {
  await sleep(delay)
  return 'bar'
}

function Preload({ children }: { children?: React.ReactNode }) {
  const [ready, setReady] = useState(false)

  useEffect(() => {
    preload(keyA, fetcherA)
    preload(keyB, fetcherB)
    setReady(true)
  }, [])

  return ready ? <>{children}</> : null
}

function Content() {
  const { data: first } = useSWR(keyA, fetcherA, { suspense: true })
  const { data: second } = useSWR(keyB, fetcherB, { suspense: true })

  useEffect(() => {
    if (!first || !second) {
      return
    }
  }, [first, second])

  return (
    <div style={{ display: 'grid', gap: '0.5rem' }}>
      <div data-testid="data">
        data:{first}:{second}
      </div>
    </div>
  )
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Preload>
        <Suspense fallback={<div data-testid="fallback">Loading...</div>}>
          <Content />
        </Suspense>
      </Preload>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-preload-basic/page.tsx
================================================
'use client'

import { Suspense, useEffect, useState } from 'react'
import useSWR, { preload } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

const key = 'render-preload-basic'
let fetchCount = 0

async function fetcher() {
  await sleep(100)
  fetchCount += 1
  return 'foo'
}

function Preload({ children }: { children?: React.ReactNode }) {
  const [isPreloaded, setIsPreloaded] = useState(false)

  useEffect(() => {
    preload(key, fetcher)
    setIsPreloaded(true)
  }, [])
  return <>{isPreloaded ? children : null}</>
}

export default function Page() {
  const { data } = useSWR(key, fetcher)
  const [count, setCount] = useState(fetchCount)

  useEffect(() => {
    setCount(fetchCount)
  }, [data])

  return (
    <OnlyRenderInClient>
      <Preload>
        <Suspense fallback={<div>Loading...</div>}>
          <div style={{ display: 'grid', gap: '0.5rem' }}>
            <div data-testid="data">data:{data ?? ''}</div>
            <div data-testid="fetch-count">fetches: {count}</div>
          </div>
        </Suspense>
      </Preload>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-promise-suspense-error/page.tsx
================================================
'use client'

import { Suspense, useMemo, useState } from 'react'
import type { ReactNode } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import useSWR, { SWRConfig } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

const key = 'render-promise-suspense-error'
const fallbackDelay = 150

function PromiseConfig({ children }: { children: ReactNode }) {
  const [fallback] = useState(() =>
    sleep(fallbackDelay).then(() => {
      throw new Error('error')
    })
  )

  const value = useMemo(() => ({ fallback: { [key]: fallback } }), [fallback])

  return <SWRConfig value={value}>{children}</SWRConfig>
}

function Content() {
  const { data } = useSWR<string>(key)
  return <div data-testid="data">data:{data ?? 'undefined'}</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <PromiseConfig>
        <ErrorBoundary
          fallbackRender={({ error }) => (
            <div data-testid="error">{error.message}</div>
          )}
        >
          <Suspense fallback={<div data-testid="fallback">loading</div>}>
            <Content />
          </Suspense>
        </ErrorBoundary>
      </PromiseConfig>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-promise-suspense-resolve/page.tsx
================================================
'use client'

import { Suspense, useMemo, useState } from 'react'
import type { ReactNode } from 'react'
import useSWR, { SWRConfig } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'
import { useDebugHistory } from '~/lib/use-debug-history'

const key = 'render-promise-suspense-resolve'
const fallbackDelay = 150
const fetchDelay = 200

async function fetcher() {
  await sleep(fetchDelay)
  return 'new data'
}

function PromiseConfig({ children }: { children: ReactNode }) {
  const [fallback] = useState(() =>
    sleep(fallbackDelay).then(() => 'initial data')
  )

  const value = useMemo(() => ({ fallback: { [key]: fallback } }), [fallback])

  return <SWRConfig value={value}>{children}</SWRConfig>
}

function Content() {
  const { data } = useSWR(key, fetcher)
  const historyRef = useDebugHistory(data, 'history:')

  return (
    <div style={{ display: 'grid', gap: '0.5rem' }}>
      <div data-testid="data">data:{data ?? 'undefined'}</div>
      <div data-testid="history" ref={historyRef}></div>
    </div>
  )
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <PromiseConfig>
        <Suspense fallback={<div data-testid="fallback">loading</div>}>
          <Content />
        </Suspense>
      </PromiseConfig>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-promise-suspense-shared/page.tsx
================================================
'use client'

import { Suspense, useMemo, useState } from 'react'
import type { ReactNode } from 'react'
import useSWR, { SWRConfig } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

const key = 'render-promise-suspense-shared'
const fallbackDelay = 150

function PromiseConfig({ children }: { children: ReactNode }) {
  const [fallback] = useState(() => sleep(fallbackDelay).then(() => 'value'))
  const value = useMemo(() => ({ fallback: { [key]: fallback } }), [fallback])
  return <SWRConfig value={value}>{children}</SWRConfig>
}

function Item({ id }: { id: string }) {
  const { data } = useSWR<string>(key)
  return <div data-testid={`data-${id}`}>data:{data ?? 'undefined'}</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <PromiseConfig>
        <Suspense fallback={<div data-testid="fallback">loading</div>}>
          <div style={{ display: 'grid', gap: '0.5rem' }}>
            <Item id="first" />
            <Item id="second" />
          </div>
        </Suspense>
      </PromiseConfig>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-avoid-rerender/page.tsx
================================================
'use client'

import { Suspense, useRef } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchValue() {
  await sleep(150)
  return 'SWR'
}

function Section() {
  const startCountRef = useRef(0)
  const dataCountRef = useRef(0)
  const prevDataRef = useRef<any>(Symbol('initial'))

  startCountRef.current += 1

  const { data } = useSWR('render-suspense-avoid-rerender', fetchValue, {
    suspense: true
  })

  if (data !== prevDataRef.current) {
    if (data !== undefined) {
      dataCountRef.current += 1
    }
    prevDataRef.current = data
  }

  return (
    <div style={{ display: 'grid', gap: '0.5rem' }}>
      <div data-testid="start-count">
        start renders: {startCountRef.current}
      </div>
      <div data-testid="data-count">data renders: {dataCountRef.current}</div>
      <div data-testid="data">{data}</div>
    </div>
  )
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div data-testid="fallback">fallback</div>}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-cached-error/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import useSWR, { SWRConfig } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchWithError(): Promise<string> {
  await sleep(150)
  throw new Error('error')
}

function Section() {
  const { data, error } = useSWR<string>(
    'render-suspense-cached-error',
    fetchWithError,
    { suspense: true }
  )

  return (
    <div data-testid="result">
      data: {data ?? ''}, error: {error?.message ?? ''}
    </div>
  )
}

const cache = new Map()
cache.set('render-suspense-cached-error', 'hello')
const cacheProvider = () => cache

function ErrorFallback(_: { error: Error; resetErrorBoundary: () => void }) {
  return <div data-testid="error">error boundary</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <SWRConfig
        value={{
          provider: cacheProvider
        }}
      >
        <ErrorBoundary FallbackComponent={ErrorFallback}>
          <Suspense fallback={<div data-testid="fallback">fallback</div>}>
            <Section />
          </Suspense>
        </ErrorBoundary>
      </SWRConfig>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-error/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import useSWR from 'swr'
import { ErrorBoundary } from 'react-error-boundary'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchWithError(): Promise<string> {
  await sleep(120)
  throw new Error('error')
}

function Section() {
  const { data } = useSWR('render-suspense-error', fetchWithError, {
    suspense: true
  })

  return <div data-testid="data">{data}</div>
}

function ErrorFallback(_: { error: Error; resetErrorBoundary: () => void }) {
  return <div data-testid="error">error boundary</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <ErrorBoundary FallbackComponent={ErrorFallback}>
        <Suspense fallback={<div data-testid="fallback">fallback</div>}>
          <Section />
        </Suspense>
      </ErrorBoundary>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-fallback/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchGreeting() {
  await sleep(500)
  return 'SWR'
}

function Section() {
  const { data } = useSWR<string>('render-suspense-fallback', fetchGreeting, {
    suspense: true
  })

  return <div data-testid="data">{data}</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div data-testid="fallback">fallback</div>}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-fetcher/page.tsx
================================================
'use client'

import { Suspense, useRef, useState } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

function Section() {
  const [prefix, setPrefix] = useState('a')
  const fetcherRef = useRef<() => Promise<string>>(() =>
    sleep(100).then(() => 'foo')
  )

  const { data } = useSWR(
    `render-suspense-fetcher-${prefix}`,
    () => fetcherRef.current(),
    { suspense: true }
  )

  return (
    <div style={{ display: 'grid', gap: '0.75rem' }}>
      <div data-testid="data">data:{data}</div>
      <div style={{ display: 'flex', gap: '0.5rem' }}>
        <button
          type="button"
          onClick={() => {
            fetcherRef.current = () => sleep(100).then(() => 'foo')
            setPrefix('a')
          }}
          data-testid="set-foo"
        >
          set foo
        </button>
        <button
          type="button"
          onClick={() => {
            fetcherRef.current = () => sleep(100).then(() => 'bar')
            setPrefix('b')
          }}
          data-testid="set-bar"
        >
          set bar
        </button>
      </div>
    </div>
  )
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div data-testid="fallback">loading</div>}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-infinite-preload/page.tsx
================================================
'use client'

import { Suspense, useEffect, useState } from 'react'
import useSWRInfinite from 'swr/infinite'
import { preload } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

const baseKey = 'render-suspense-infinite-preload'
const getKey = (index: number) => `${baseKey}-${index}`

let fetchCount = 0
let preloaded = false
let fallbackRenderCount = 0

async function fetcher(key: string) {
  fetchCount += 1
  await sleep(100)
  return `${key}-value`
}

if (!preloaded) {
  preloaded = true
  preload(getKey(0), fetcher)
}

function Section() {
  const { data } = useSWRInfinite(index => getKey(index), fetcher, {
    suspense: true
  })

  const [count, setCount] = useState(fetchCount)
  const [fallbackCount, setFallbackCount] = useState(fallbackRenderCount)
  useEffect(() => {
    setCount(fetchCount)
    setFallbackCount(fallbackRenderCount)
  }, [data])

  const firstPage = data?.[0]

  return (
    <div style={{ display: 'grid', gap: '0.5rem' }}>
      <div data-testid="data">{firstPage ?? 'no-data'}</div>
      <div data-testid="fetch-count">fetches: {count}</div>
      <div data-testid="fallback-count">fallback renders: {fallbackCount}</div>
    </div>
  )
}

function Fallback() {
  fallbackRenderCount += 1
  return <div data-testid="fallback">loading</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<Fallback />}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-initial-data/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import useSWR from 'swr'

const fetcher = () => 'SWR'

function PageContent() {
  const { data } = useSWR('render-suspense-initial-data', fetcher, {
    fallbackData: 'Initial',
    suspense: true
  })

  return <div data-testid="data">hello, {data}</div>
}

export default function Page() {
  return (
    <Suspense fallback={<div data-testid="fallback">fallback</div>}>
      <PageContent />
    </Suspense>
  )
}


================================================
FILE: e2e/site/app/render-suspense-keep-previous/page.tsx
================================================
'use client'

import { Suspense, useState } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchValue(key: string) {
  await sleep(150)
  return `data:${key}`
}

function Section() {
  const [query, setQuery] = useState('origin')
  const { data } = useSWR(query, fetchValue, {
    suspense: true,
    keepPreviousData: true
  })

  return (
    <div style={{ display: 'grid', gap: '0.5rem' }}>
      <div data-testid="data">{data}</div>
      <div style={{ display: 'flex', gap: '0.5rem' }}>
        <button
          type="button"
          onClick={() => setQuery('origin')}
          data-testid="set-origin"
        >
          origin
        </button>
        <button
          type="button"
          onClick={() => setQuery('next')}
          data-testid="set-next"
        >
          next
        </button>
      </div>
    </div>
  )
}

function Fallback() {
  return <div data-testid="fallback">loading</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<Fallback />}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-key-change/page.tsx
================================================
'use client'

import { Suspense, useState } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchValue(key: string) {
  await sleep(120)
  return key
}

function Section() {
  const [key, setKey] = useState('initial')
  const { data } = useSWR(
    key ? `render-suspense-key-change-${key}` : null,
    fetchValue,
    { suspense: true }
  )

  return (
    <div>
      <div data-testid="data">data: {data}</div>
      <button
        type="button"
        onClick={() => setKey('updated')}
        data-testid="toggle"
      >
        change
      </button>
    </div>
  )
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div data-testid="fallback">fallback</div>}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-multiple-fallbacks/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchValue(key: string) {
  if (key === 'render-suspense-multiple-fallbacks-1') {
    await sleep(50)
    return 1
  }
  await sleep(140)
  return 2
}

function Section() {
  const { data: v1 } = useSWR<number>(
    'render-suspense-multiple-fallbacks-1',
    fetchValue,
    { suspense: true }
  )
  const { data: v2 } = useSWR<number>(
    'render-suspense-multiple-fallbacks-2',
    fetchValue,
    { suspense: true }
  )

  return <div data-testid="data">{(v1 ?? 0) + (v2 ?? 0)}</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div data-testid="fallback">fallback</div>}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-no-revalidate/page.tsx
================================================
'use client'

import { Suspense, useLayoutEffect, useState } from 'react'
import useSWR, { preload } from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchFreshValue() {
  await sleep(120)
  return 'fresh'
}

function Preload({ children }: { children?: React.ReactNode }) {
  const [isMounted, setIsMounted] = useState(false)
  useLayoutEffect(() => {
    preload('render-suspense-no-revalidate', () => 'cached')
    setIsMounted(true)
  }, [])
  return isMounted ? <>{children}</> : null
}

function Section() {
  const { data } = useSWR('render-suspense-no-revalidate', fetchFreshValue, {
    suspense: true,
    revalidateIfStale: false
  })

  return <div data-testid="data">data: {data}</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Preload>
        <Suspense fallback={<div data-testid="fallback">fallback</div>}>
          <Section />
        </Suspense>
      </Preload>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-non-promise/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'

function Section() {
  const { data } = useSWR('render-suspense-non-promise', () => 'hello', {
    suspense: true
  })

  return <div data-testid="data">{data}</div>
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div data-testid="fallback">fallback</div>}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-null-key/page.tsx
================================================
'use client'

import { Suspense, useState } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchValue(key: string) {
  await sleep(150)
  return key
}

function Result({ query }: { query: string }) {
  const { data } = useSWR(
    query ? `render-suspense-null-key-${query}` : null,
    fetchValue,
    {
      suspense: true
    }
  )

  const text = data ?? 'render-suspense-null-key-nodata'

  return <div data-testid="data">{text}</div>
}

export default function Page() {
  const [query, setQuery] = useState('123')

  return (
    <OnlyRenderInClient>
      <div style={{ display: 'grid', gap: '0.5rem' }}>
        <div style={{ display: 'flex', gap: '0.5rem' }}>
          <button
            type="button"
            onClick={() => setQuery('123')}
            data-testid="set-123"
          >
            set 123
          </button>
          <button
            type="button"
            onClick={() => setQuery('')}
            data-testid="set-empty"
          >
            clear
          </button>
          <button
            type="button"
            onClick={() => setQuery('456')}
            data-testid="set-456"
          >
            set 456
          </button>
        </div>
        <Suspense fallback={null}>
          <Result query={query} />
        </Suspense>
      </div>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/render-suspense-same-data/page.tsx
================================================
'use client'

import { Suspense, useState } from 'react'
import useSWR from 'swr'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

async function fetchValue() {
  await sleep(120)
  return '123'
}

function Section() {
  const [step, setStep] = useState(1)
  const { data } = useSWR(`render-suspense-same-data-${step}`, fetchValue, {
    suspense: true
  })

  return (
    <div>
      <div data-testid="data">
        data: {data},{step}
      </div>
      <button
        type="button"
        data-testid="increment"
        onClick={() => setStep(current => current + 1)}
      >
        next
      </button>
    </div>
  )
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div data-testid="fallback">fallback</div>}>
        <Section />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/server-prefetch-warning/page.tsx
================================================
'use client'

import { useEffect, useState } from 'react'
import useSWR, { SWRConfig } from 'swr'

const fetcher = () => 'SWR'

function Content() {
  const [hydrated, setHydrated] = useState(false)
  useEffect(() => {
    setHydrated(true)
  }, [])

  useSWR('ssr:1', fetcher)
  useSWR('ssr:2', fetcher)
  useSWR('ssr:3', fetcher, { strictServerPrefetchWarning: false })
  useSWR('ssr:4', fetcher, { fallbackData: 'SWR' })
  useSWR('ssr:5', fetcher)

  return (
    <div style={{ display: 'grid', gap: '0.5rem' }}>
      <div data-testid="title">server-prefetch-warning</div>
      <div data-testid="hydration-state">{hydrated ? 'hydrated' : 'ssr'}</div>
    </div>
  )
}

export default function ServerPrefetchWarningPage() {
  return (
    <SWRConfig
      value={{
        strictServerPrefetchWarning: true,
        fallback: {
          'ssr:5': 'SWR'
        }
      }}
    >
      <Content />
    </SWRConfig>
  )
}


================================================
FILE: e2e/site/app/suspense-after-preload/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const RemoteData = dynamic(() => import('./remote-data'), {
  ssr: false
})

export default function HomePage() {
  return (
    <Suspense fallback={<div>loading component</div>}>
      <RemoteData></RemoteData>
    </Suspense>
  )
}


================================================
FILE: e2e/site/app/suspense-after-preload/remote-data.tsx
================================================
'use client'
import { Suspense, useState } from 'react'
import useSWR from 'swr'
import { preload } from 'swr'

const fetcher = ([key, delay]: [key: string, delay: number]) =>
  new Promise<string>(r => {
    setTimeout(r, delay, key)
  })

const key = ['suspense-after-preload', 300] as const
const useRemoteData = () =>
  useSWR(key, fetcher, {
    suspense: true
  })

const Demo = () => {
  const { data } = useRemoteData()
  return <div>{data}</div>
}

function Comp() {
  const [show, toggle] = useState(false)

  return (
    <div className="App">
      <button
        onClick={async () => {
          preload(key, fetcher)
          toggle(!show)
        }}
      >
        preload
      </button>
      {show ? (
        <Suspense fallback={<div>loading</div>}>
          <Demo />
        </Suspense>
      ) : null}
    </div>
  )
}

export default Comp


================================================
FILE: e2e/site/app/suspense-fallback/layout.tsx
================================================
import { SWRConfig } from 'swr'

function createPromiseData(data: any, timeout: number) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(data)
    }, timeout)
  })
}

export default function Layout({ children }: { children: React.ReactNode }) {
  const fallback = {
    '/api/promise': createPromiseData({ value: 'async promise' }, 2000)
  }

  return <SWRConfig value={{ fallback }}>{children}</SWRConfig>
}


================================================
FILE: e2e/site/app/suspense-fallback/promise/page.tsx
================================================
'use client'

import useSWR from 'swr'

export default function Page() {
  const { data, isLoading } = useSWR('/api/promise')

  return <div>{isLoading ? 'loading...' : data?.value}</div>
}


================================================
FILE: e2e/site/app/suspense-infinite-get-key/page.tsx
================================================
'use client'

import { Suspense, useState } from 'react'
import useSWRInfinite from 'swr/infinite'
import { OnlyRenderInClient } from '~/component/only-render-in-client'
import { sleep } from '~/lib/sleep'

const DATA: Record<string, string[]> = {
  'suspense-key-a': ['A1', 'A2', 'A3'],
  'suspense-key-b': ['B1', 'B2', 'B3']
}

async function fetchList(key: string) {
  await sleep(150)
  return DATA[key]
}

function List() {
  const [status, setStatus] = useState<'a' | 'b'>('a')
  const { data, setSize } = useSWRInfinite(
    () => (status === 'a' ? 'suspense-key-a' : 'suspense-key-b'),
    fetchList,
    { suspense: true }
  )

  return (
    <>
      <div data-testid="data">data: {String(data)}</div>
      <button
        type="button"
        onClick={() => {
          setStatus('b')
          void setSize(1)
        }}
      >
        mutate
      </button>
    </>
  )
}

export default function Page() {
  return (
    <OnlyRenderInClient>
      <Suspense fallback={<div>loading</div>}>
        <List />
      </Suspense>
    </OnlyRenderInClient>
  )
}


================================================
FILE: e2e/site/app/suspense-retry/manual-retry.tsx
================================================
'use client'
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { useRemoteData, preloadRemote } from './use-remote-data'

const Demo = () => {
  const { data } = useRemoteData()
  return <div>data: {data}</div>
}

function Fallback({ resetErrorBoundary }: any) {
  return (
    <div role="alert">
      <p>Something went wrong</p>
      <button
        onClick={() => {
          resetErrorBoundary()
        }}
      >
        retry
      </button>
    </div>
  )
}

function RemoteData() {
  return (
    <div className="App">
      <ErrorBoundary
        FallbackComponent={Fallback}
        onReset={() => {
          preloadRemote()
        }}
      >
        <Suspense fallback={<div>loading</div>}>
          <Demo />
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

export default RemoteData


================================================
FILE: e2e/site/app/suspense-retry/page.tsx
================================================
'use client'

import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const RemoteData = dynamic(() => import('./manual-retry'), {
  ssr: false
})

export default function HomePage() {
  return (
    <Suspense fallback={<div>loading component</div>}>
      <RemoteData></RemoteData>
    </Suspense>
  )
}


================================================
FILE: e2e/site/app/suspense-retry/use-remote-data.ts
================================================
'use client'
import useSWR from 'swr'
import { preload } from 'swr'

let count = 0
const fetcher = () => {
  count++
  if (count === 1) return Promise.reject('wrong')
  return fetch('/api/retry')
    .then(r => r.json())
    .then(r => r.name)
}

const key = 'manual-retry-18-3'

export const useRemoteData = () =>
  useSWR(key, fetcher, {
    suspense: true
  })

export const preloadRemote = () => preload(key, fetcher)


================================================
FILE: e2e/site/app/suspense-undefined-key/page.tsx
================================================
'use client'

import { Suspense, useReducer } from 'react'
import useSWR from 'swr'

const fetcher = async (key: string) => {
  // Add a small delay to simulate network request
  await new Promise(resolve => setTimeout(resolve, 100))
  return 'SWR'
}

const Section = ({ trigger }: { trigger: boolean }) => {
  const { data } = useSWR(trigger ? 'test-key' : undefined, fetcher, {
    suspense: true
  })
  return <div>{data || 'empty'}</div>
}

export default function Page() {
  const [trigger, toggle] = useReducer(x => !x, false)

  return (
    <div>
      <button onClick={toggle}>toggle</button>
      <Suspense fallback={<div>fallback</div>}>
        <Section trigger={trigger} />
      </Suspense>
    </div>
  )
}


================================================
FILE: e2e/site/component/manual-retry-mutate.tsx
================================================
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import useSWR from 'swr'
import { mutate } from 'swr'

let count = 0
export const fetcher = () => {
  count++
  if (count === 1) return Promise.reject('wrong')
  return fetch('/api/retry')
    .then(r => r.json())
    .then(r => r.name)
}

const key = 'manual-retry-mutate'

export const useRemoteData = () =>
  useSWR(key, fetcher, {
    suspense: true
  })
const Demo = () => {
  const { data } = useRemoteData()
  return <div>data: {data}</div>
}

function Fallback({ resetErrorBoundary }: any) {
  return (
    <div role="alert">
      <p>Something went wrong</p>
      <button
        onClick={async () => {
          await mutate(key, fetcher)
          resetErrorBoundary()
        }}
      >
        retry
      </button>
    </div>
  )
}

function RemoteData() {
  return (
    <div className="App">
      <ErrorBoundary FallbackComponent={Fallback}>
        <Suspense fallback={<div>loading</div>}>
          <Demo />
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

export default RemoteData


================================================
FILE: e2e/site/component/manual-retry.tsx
================================================
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { useRemoteData, preloadRemote } from './use-remote-data'

const Demo = () => {
  const { data } = useRemoteData()
  return <div>data: {data}</div>
}

function Fallback({ resetErrorBoundary }: any) {
  return (
    <div role="alert">
      <p>Something went wrong</p>
      <button
        onClick={() => {
          resetErrorBoundary()
        }}
      >
        retry
      </button>
    </div>
  )
}

function RemoteData() {
  return (
    <div className="App">
      <ErrorBoundary
        FallbackComponent={Fallback}
        onReset={() => {
          preloadRemote()
        }}
      >
        <Suspense fallback={<div>loading</div>}>
          <Demo />
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

export default RemoteData


================================================
FILE: e2e/site/component/only-render-in-client.tsx
================================================
'use client'
import React from 'react'

// This component ensures its children are only rendered on the client side.
export function OnlyRenderInClient({
  children
}: {
  children?: React.ReactNode
}) {
  const [isClient, setIsClient] = React.useState(false)
  React.useEffect(() => {
    setIsClient(true)
  }, [])
  if (!isClient) {
    return null
  }
  return <>{children}</>
}


================================================
FILE: e2e/site/component/use-remote-data.ts
================================================
import useSWR from 'swr'
import { preload } from 'swr'

let count = 0
export const fetcher = () => {
  count++
  if (count === 1) return Promise.reject('wrong')
  return fetch('/api/retry')
    .then(r => r.json())
    .then(r => r.name)
}

const key = 'manual-retry-18-2'

export const useRemoteData = () =>
  useSWR(key, fetcher, {
    suspense: true
  })

export const preloadRemote = () => preload(key, fetcher)


================================================
FILE: e2e/site/lib/sleep.ts
================================================
export function sleep(ms: number) {
  return new Promise<void>(resolve => setTimeout(resolve, ms))
}


================================================
FILE: e2e/site/lib/use-debug-history.ts
================================================
'use client'
import { useEffect, useRef } from 'react'

export const useDebugHistory = <T>(data: T, prefix = '') => {
  const dataRef = useRef<T[]>([])
  const debugRef = useRef<HTMLDivElement>(null)
  useEffect(() => {
    dataRef.current.push(data)
    if (debugRef.current) {
      debugRef.current.innerText = `${prefix}${JSON.stringify(dataRef.current)}`
    }
  }, [data, prefix])
  return debugRef
}


================================================
FILE: e2e/site/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
import './.next/types/routes.d.ts'

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.


================================================
FILE: e2e/site/next.config.js
================================================
/** @type {import('next').NextConfig} */
const nextConfig = {}

module.exports = nextConfig


================================================
FILE: e2e/site/package.json
================================================
{
  "name": "site",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@types/node": "^22.19.1",
    "@types/react": "^19.2.7",
    "@types/react-dom": "^19.2.7",
    "next": "^16.0.10",
    "react": "^19.1.4",
    "react-dom": "^19.1.4",
    "typescript": "5.9.3",
    "swr": "link:../../"
  }
}


================================================
FILE: e2e/site/pages/api/data.ts
================================================
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'

type Data = {
  name: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  res.status(200).json({ name: 'SSR Works' })
}


================================================
FILE: e2e/site/pages/api/retry.ts
================================================
import type { NextApiRequest, NextApiResponse } from 'next'

type Data = {
  name: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  res.status(200).json({ name: 'SWR suspense retry works' })
}


================================================
FILE: e2e/site/pages/suspense-retry-19.tsx
================================================
import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const RemoteData = dynamic(() => import('../component/manual-retry'), {
  ssr: false
})

export default function HomePage() {
  return (
    <Suspense fallback={<div>loading component</div>}>
      <RemoteData></RemoteData>
    </Suspense>
  )
}


================================================
FILE: e2e/site/pages/suspense-retry-mutate.tsx
================================================
import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const RemoteData = dynamic(() => import('../component/manual-retry-mutate'), {
  ssr: false
})

export default function HomePage() {
  return (
    <Suspense fallback={<div>loading component</div>}>
      <RemoteData></RemoteData>
    </Suspense>
  )
}


================================================
FILE: e2e/site/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts",
    ".next/dev/types/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}


================================================
FILE: e2e/test/concurrent-transition.test.ts
================================================
import { test, expect } from '@playwright/test'

test.describe('concurrent rendering transitions', () => {
  test('should pause when changing the key inside a transition', async ({
    page
  }) => {
    // Navigate to the test page
    await page.goto('./concurrent-transition', { waitUntil: 'networkidle' })

    // Wait for page to be fully loaded and interactive
    await expect(page.getByTestId('pending-state')).toContainText('isPending:0')

    // Wait for initial data to load
    await expect(page.getByTestId('data-content')).toContainText(
      'data:initial-key'
    )

    // Ensure the component is in a stable state before triggering transition
    await page.waitForTimeout(100)

    // Click to trigger transition
    await page.getByTestId('transition-trigger').click()

    // Verify transition starts - isPending becomes true
    await expect(page.getByTestId('pending-state')).toContainText('isPending:1')

    // During transition: data should still show old value (this is the key behavior)
    // In React 19, this behavior should be more consistent
    await expect(page.getByTestId('data-content')).toContainText(
      'data:initial-key'
    )

    // Wait for transition to complete
    await expect(page.getByTestId('pending-state')).toContainText('isPending:0')
    await expect(page.getByTestId('data-content')).toContainText('data:new-key')
  })
})


================================================
FILE: e2e/test/initial-render.test.ts
================================================
import { test, expect } from '@playwright/test'

test.describe('rendering', () => {
  test('suspense with preload', async ({ page }) => {
    await page.goto('./suspense-after-preload', { waitUntil: 'commit' })
    await page.getByRole('button', { name: 'preload' }).click()
    await expect(page.getByText('suspense-after-preload')).toBeVisible()
  })

  test('should be able to retry in suspense with react 19 (app router)', async ({
    page
  }) => {
    await page.goto('./suspense-retry', { waitUntil: 'commit' })
    await expect(page.getByText('Something went wrong')).toBeVisible()
    await page.getByRole('button', { name: 'retry' }).click()
    await expect(page.getByText('data: SWR suspense retry works')).toBeVisible()
  })

  test('should be able to retry in suspense with react 19 (pages router)', async ({
    page
  }) => {
    await page.goto('./suspense-retry-19', { waitUntil: 'commit' })
    await expect(page.getByText('Something went wrong')).toBeVisible()
    await page.getByRole('button', { name: 'retry' }).click()
    await expect(page.getByText('data: SWR suspense retry works')).toBeVisible()
  })

  test('should be able to retry in suspense with mutate', async ({ page }) => {
    await page.goto('./suspense-retry-mutate', { waitUntil: 'commit' })
    await expect(page.getByText('Something went wrong')).toBeVisible()
    await page.getByRole('button', { name: 'retry' }).click()
    await expect(page.getByText('data: SWR suspense retry works')).toBeVisible()
  })

  test('should be able to use `unstable_serialize` in server component', async ({
    page
  }) => {
    await page.goto('./react-server-entry', { waitUntil: 'commit' })
    await expect(page.getByText('unstable_serialize: useSWR')).toBeVisible()
    await expect(
      page.getByText('infinite_unstable_serialize: $inf$useSWRInfinite')
    ).toBeVisible()
  })

  test('swr should not cause extra rerenders', async ({ page }) => {
    const renderLogs: string[] = []
    await page.exposeFunction('consoleCount', (msg: string) => {
      renderLogs.push(msg)
    })
    await page.addInitScript(() => {
      const originalConsoleCount = console.count
      console.count = (label?: string) => {
        // @ts-ignore
        window.consoleCount(label)
        originalConsoleCount.call(console, label)
      }
    })
    await page.goto('./render-count', { waitUntil: 'commit' })
    // wait a bit to ensure no extra renders happen
    await page.waitForTimeout(500)
    const renderCount = renderLogs.filter(log => log === 'render').length
    expect(renderCount).toBe(1)
  })
})


================================================
FILE: e2e/test/issue-2702-too-many-hooks.ts
================================================
import { test, expect } from '@playwright/test'

test.describe('issue 2702', () => {
  test('should not crash with too many hooks', async ({ page }) => {
    // Navigate to the test page
    await page.goto('./issue-2702', { waitUntil: 'networkidle' })

    // Wait for the page to be fully loaded and interactive
    await expect(page.getByText('fetching')).toBeVisible()

    // Verify that the component renders correctly
    await expect(page.getByText('a,b')).toBeVisible()
  })
})


================================================
FILE: e2e/test/mutate-server-action.test.ts
================================================
import { test, expect } from '@playwright/test'

test('mutate-server-action', async ({ page }) => {
  await page.goto('./mutate-server-action')
  await page.getByRole('button', { name: 'mutate' }).click()
  await expect(page.getByText('isMutating: true')).toBeVisible()
  await expect(page.getByText('data: ')).toBeVisible()
  await page.waitForTimeout(500)
  await expect(page.getByText('isMutating: false')).toBeVisible()
  await expect(page.getByText('data: 10086')).toBeVisible()
})


================================================
FILE: e2e/test/perf.test.ts
================================================
import { test, expect } from '@playwright/test'

test.describe('performance', () => {
  test('should render expensive component within 1 second after checkbox click', async ({
    page
  }) => {
    // Navigate to the perf page
    await page.goto('./perf', { waitUntil: 'load' })

    // Inject performance measurement into the page
    await page.evaluate(() => {
      const checkboxInput = document.querySelector(
        'input[type="checkbox"]'
      ) as HTMLInputElement
      let expensiveComponentContainer: HTMLElement | null = null
      const targetChildCount = 10_000
      let startTime = 0

      // Track when React starts and completes rendering
      const observer = new MutationObserver(mutations => {
        for (const mutation of mutations) {
          const addedNodes = Array.from(mutation.addedNodes)
          for (const node of addedNodes) {
            if (node instanceof HTMLElement) {
              const h2 = node.querySelector?.('h2')
              if (h2?.textContent === 'Expensive Component') {
                expensiveComponentContainer = node
                console.log('Found Expensive Component container')
              }
            }
          }

          if (expensiveComponentContainer) {
            const renderedComponents =
              expensiveComponentContainer.querySelectorAll('div > div').length

            if (renderedComponents % 1000 === 0 && renderedComponents > 0) {
              console.log(`Rendered ${renderedComponents} components...`)
            }

            if (renderedComponents >= targetChildCount) {
              console.log(`All ${renderedComponents} components rendered!`)

              // Use requestAnimationFrame to ensure the browser has painted
              requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                  // Double RAF to ensure we're after the paint
                  window.performance.mark('expensive-component-painted')
                  const paintTime = performance.now() - startTime
                  console.log(
                    `Total time including paint: ${paintTime.toFixed(2)}ms`
                  )
                })
              })

              observer.disconnect()
            }
          }
        }
      })

      observer.observe(document.body, { childList: true, subtree: true })

      // Capture more precise timing
      checkboxInput.addEventListener(
        'click',
        () => {
          startTime = performance.now()
          window.performance.mark('state-change-start')
          console.log('Checkbox clicked, state change started')
        },
        { once: true }
      )
    })

    // Find and click the checkbox
    const checkbox = page.locator('input[type="checkbox"]')
    await checkbox.click()

    // Wait for the expensive component to be fully rendered
    const expensiveComponentHeading = page.locator(
      'h2:has-text("Expensive Component")'
    )
    await expect(expensiveComponentHeading).toBeVisible({ timeout: 60_000 })

    // Wait for all components and paint to complete
    await page.waitForFunction(
      () => {
        return (
          window.performance.getEntriesByName('expensive-component-painted')
            .length > 0
        )
      },
      { timeout: 5000 }
    )

    // Get the render time from state change to paint
    const renderTime = await page.evaluate(() => {
      const startMark =
        window.performance.getEntriesByName('state-change-start')[0]
      const paintMark = window.performance.getEntriesByName(
        'expensive-component-painted'
      )[0]

      if (!startMark || !paintMark) {
        throw new Error('Performance marks not found')
      }

      return paintMark.startTime - startMark.startTime
    })

    // Assert that the rendering took less than 1 second (1000ms)
    expect(renderTime).toBeLessThan(1000)
  })
})


================================================
FILE: e2e/test/preload-scenarios.test.ts
================================================
import { expect, test } from '@playwright/test'

test.describe('preload scenarios', () => {
  test('preloads fetcher before mount', async ({ page }) => {
    await page.goto('./render-preload-basic', { waitUntil: 'commit' })

    await expect(page.getByTestId('data')).toHaveText('data:foo')
    await expect(page.getByTestId('fetch-count')).toHaveText('fetches: 1')
  })

  test('avoids suspense waterfall when preloading multiple resources', async ({
    page
  }) => {
    await page.goto('./render-preload-avoid-waterfall', {
      waitUntil: 'commit'
    })
    await expect(page.getByTestId('fallback')).toBeVisible()
    await page.waitForTimeout(200)
    await expect(page.getByTestId('fallback')).not.toBeVisible()
    await expect(page.getByTestId('data')).toHaveText('data:foo:bar')
  })
})


================================================
FILE: e2e/test/promise-scenarios.test.ts
================================================
import { expect, test } from '@playwright/test'

test.describe('promise scenarios', () => {
  test('suspends while resolving fallback promise', async ({ page }) => {
    await page.goto('./render-promise-suspense-resolve', {
      waitUntil: 'commit'
    })

    await expect(page.getByTestId('fallback')).toBeVisible()

    const history = page.getByTestId('history')

    await expect(history).toContainText('"initial data"')

    await expect(page.getByTestId('data')).toHaveText('data:new data')
    await expect(history).toHaveText('history:["initial data","new data"]')

    await expect(page.getByTestId('fallback')).toHaveCount(0)
  })

  test('surfaces error boundary when fallback promise rejects', async ({
    page
  }) => {
    await page.goto('./render-promise-suspense-error', {
      waitUntil: 'commit'
    })

    await expect(page.getByTestId('fallback')).toBeVisible()

    await expect(page.getByTestId('error')).toHaveText('error')
    await expect(page.getByTestId('fallback')).toHaveCount(0)
    await expect(page.getByTestId('data')).toHaveCount(0)
  })

  test('resolves shared fallback promise once for multiple consumers', async ({
    page
  }) => {
    await page.goto('./render-promise-suspense-shared', {
      waitUntil: 'commit'
    })

    await expect(page.getByTestId('fallback')).toBeVisible()

    await expect(page.getByTestId('data-first')).toHaveText('data:value')
    await expect(page.getByTestId('data-second')).toHaveText('data:value')
    await expect(page.getByTestId('fallback')).toHaveCount(0)
  })
})


================================================
FILE: e2e/test/server-prefetch-warning.test.ts
================================================
import { test, expect } from '@playwright/test'

const warningForKey = (key: string) =>
  `Missing pre-initiated data for serialized key "${key}" during server-side rendering. Data fetching should be initiated on the server and provided to SWR via fallback data. You can set "strictServerPrefetchWarning: false" to disable this warning.`

test.describe('strictServerPrefetchWarning', () => {
  test('warns on hydration when data is missing', async ({ page }) => {
    const warnings: string[] = []

    page.on('console', msg => {
      if (msg.type() !== 'warning') return
      const text = msg.text()
      if (text.includes('Missing pre-initiated data')) {
        warnings.push(text)
      }
    })

    await page.goto('./server-prefetch-warning', { waitUntil: 'commit' })
    await expect(page.getByTestId('hydration-state')).toHaveText('hydrated')

    await expect.poll(() => warnings.length).toBe(2)
    expect(warnings).toEqual(
      expect.arrayContaining([warningForKey('ssr:1'), warningForKey('ssr:2')])
    )
    expect(warnings).toHaveLength(2)
  })
})


================================================
FILE: e2e/test/stream-ssr.test.ts
================================================
import { test, expect } from '@playwright/test'

test.describe('Stream SSR', () => {
  test('Basic SSR', async ({ page }) => {
    const log: any[] = []
    await page.exposeFunction('consoleError', (msg: any) => log.push(msg))
    await page.addInitScript(`
      const onError = window.onerror
      window.onerror = (...args) => {
        consoleError(...args)
        onError(...args)
      }
    `)
    await page.goto('./basic-ssr', { waitUntil: 'commit' })
    await expect(page.getByText('result:undefined')).toBeVisible()
    await expect(page.getByText('result:SSR Works')).toBeVisible()
    await expect(page.getByText('history:[null,"SSR Works"]')).toBeVisible()
    expect(log).toHaveLength(0)
  })

  test('Partially Hydrate', async ({ page }) => {
    const log: any[] = []
    await page.exposeFunction('consoleError', (msg: any) => log.push(msg))
    await page.addInitScript(`
      const onError = window.onerror
      window.onerror = (...args) => {
        consoleError(...args)
        onError(...args)
      }
    `)
    await page.goto('./partially-hydrate', { waitUntil: 'commit' })
    await expect(page.getByText('first data:undefined')).toBeVisible()
    await expect(
      page.getByText('second data (delayed hydration):undefined')
    ).toBeVisible()
    await expect(page.getByText('first data:SSR Works')).toBeVisible()
    await expect(
      page.getByText('second data (delayed hydration):SSR Works')
    ).toBeVisible()
    await expect(
      page.getByText('first history:[null,"SSR Works"]')
    ).toBeVisible()
    await expect(
      page.getByText('second history:[null,"SSR Works"]')
    ).toBeVisible()
    expect(log).toHaveLength(0)
  })
})


================================================
FILE: e2e/test/suspense-fallback.test.ts
================================================
import { test, expect } from '@playwright/test'

test.describe('suspense fallback', () => {
  test('should wait for promise fallback value to be resolved', async ({
    page
  }) => {
    await page.goto('./suspense-fallback/promise', { waitUntil: 'commit' })
    await expect(page.getByText('async promise')).toBeVisible()
  })
})


================================================
FILE: e2e/test/suspense-scenarios.test.ts
================================================
import { expect, test } from '@playwright/test'

// Group all suspense-related scenarios so they run together.
test.describe('suspense scenarios', () => {
  test('waits for promise fallback value to resolve', async ({ page }) => {
    await page.goto('./suspense-fallback/promise', { waitUntil: 'commit' })
    await expect(page.getByText('async promise')).toBeVisible()
  })

  test('updates data when suspense key changes', async ({ page }) => {
    await page.goto('./suspense-infinite-get-key', { waitUntil: 'commit' })

    await expect(page.getByTestId('data')).toHaveText('data: A1,A2,A3')

    await page.getByRole('button', { name: 'mutate' }).click()

    await expect(page.getByTestId('data')).toHaveText('data: B1,B2,B3')
  })

  test('shows fallback before resolved data renders', async ({ page }) => {
    await page.goto('./render-suspense-fallback', { waitUntil: 'commit' })

    await expect(page.getByTestId('fallback')).toBeVisible()
    await expect(page.getByTestId('data')).toHaveText('SWR')
  })

  test('keeps fallback visible until multiple resources resolve', async ({
    page
  }) => {
    await page.goto('./render-suspense-multiple-fallbacks', {
      waitUntil: 'commit'
    })

    const fallback = page.getByTestId('fallback')
    await expect(fallback).toBeVisible()

    await page.waitForTimeout(80)
    await expect(fallback).toBeVisible()

    await page.waitForTimeout(120)
    await expect(page.getByTestId('data')).toHaveText('3')
  })

  test('renders synchronously resolved data without fallback', async ({
    page
  }) => {
    await page.goto('./render-suspense-non-promise', { waitUntil: 'commit' })

    await expect(page.getByTestId('fallback')).toHaveCount(0)
    await expect(page.getByTestId('data')).toHaveText('hello')
  })

  test('surfaces error boundary after suspense fallback', async ({ page }) => {
    await page.goto('./render-suspense-error', { waitUntil: 'commit' })

    await expect(page.getByTestId('fallback')).toBeVisible()
    await expect(page.getByTestId('error')).toBeVisible()
  })

  test.skip('retains cached data while surfacing errors', async ({ page }) => {
    await page.goto('./render-suspense-cached-error', { waitUntil: 'commit' })

    await expect(page.getByTestId('fallback')).toHaveCount(0)
    await expect(page.getByTestId('result')).toHaveText('data: hello, error: ')
    await expect(page.getByTestId('result')).toHaveText(
      'data: hello, error: error'
    )
  })

  test('skips revalidation when data is cached and revalidateIfStale is false', async ({
    page
  }) => {
    await page.goto('./render-suspense-no-revalidate', { waitUntil: 'commit' })

    const data = page.getByTestId('data')
    await expect(data).toHaveText('data: cached')

    await page.waitForTimeout(200)
    await expect(data).toHaveText('data: cached')
  })

  test('pauses while key changes and resumes with new data', async ({
    page
  }) => {
    await page.goto('./render-suspense-key-change', { waitUntil: 'commit' })

    const fallback = page.getByTestId('fallback')
    const data = page.getByTestId('data')

    await expect(fallback).toBeVisible()
    await expect(data).toHaveText('data: render-suspense-key-change-initial')

    await page.getByTestId('toggle').click()

    await expect(fallback).toBeVisible()
    await expect(data).toHaveText('data: render-suspense-key-change-updated')
  })

  test('renders correctly when key changes with identical data', async ({
    page
  }) => {
    await page.goto('./render-suspense-same-data', { waitUntil: 'commit' })

    const fallback = page.getByTestId('fallback')
    const data = page.getByTestId('data')

    await expect(fallback).toBeVisible()
    await expect(data).toHaveText('data: 123,1')

    await page.getByTestId('increment').click()

    await expect(fallback).toBeVisible()
    await expect(data).toHaveText('data: 123,2')
  })

  test('renders initial data when provided', async ({ page }) => {
    await page.goto('./render-suspense-initial-data', { waitUntil: 'commit' })

    await expect(page.getByTestId('fallback')).toHaveCount(0)
    await expect(page.getByTestId('data')).toHaveText('hello, Initial')
    await page.waitForTimeout(200)
    await expect(page.getByTestId('data')).toHaveText('hello, SWR')
  })

  test('avoids unnecessary re-renders', async ({ page }) => {
    await page.goto('./render-suspense-avoid-rerender', { waitUntil: 'commit' })

    await expect(page.getByTestId('fallback')).toBeVisible()
    await expect(page.getByTestId('start-count')).toHaveText('start renders: 1')

    await page.waitForTimeout(200)

    await expect(page.getByTestId('fallback')).toHaveCount(0)
    await expect(page.getByTestId('data-count')).toHaveText('data renders: 1')
    await expect(page.getByTestId('start-count')).toHaveText('start renders: 1')
  })

  test('renders fallback only once when keepPreviousData is true', async ({
    page
  }) => {
    await page.goto('./render-suspense-keep-previous', { waitUntil: 'commit' })

    const fallback = page.getByTestId('fallback')
    const data = page.getByTestId('data')

    await expect(fallback).toBeVisible()

    await expect(data).toHaveText('data:origin')
    await expect(fallback).toHaveCount(0)

    await page.getByTestId('set-next').click()

    await expect(fallback).toHaveCount(0)
    await expect(data).toHaveText('data:origin')
    await expect(data).toHaveText('data:next')
    await expect(fallback).toHaveCount(0)
  })

  test('updates fetcher reference with suspense', async ({ page }) => {
    await page.goto('./render-suspense-fetcher', { waitUntil: 'commit' })
    const fallback = page.getByTestId('fallback')

    await expect(fallback).toBeVisible()
    await page.waitForTimeout(100)
    await expect(page.getByTestId('data')).toHaveText('data:foo')

    await page.getByTestId('set-bar').click()
    await expect(fallback).toBeVisible()
    await expect(page.getByTestId('data')).toHaveText('data:bar')
  })

  test('preloads infinite data for suspense', async ({ page }) => {
    await page.goto('./render-suspense-infinite-preload', {
      waitUntil: 'commit'
    })

    await expect(page.getByTestId('data')).toHaveText(
      'render-suspense-infinite-preload-0-value'
    )
    await expect(page.getByTestId('fetch-count')).toHaveText('fetches: 1')
    await expect(page.getByTestId('fallback-count')).toHaveText(
      'fallback renders: 1'
    )
    await expect(page.getByTestId('fallback')).toHaveCount(0)
  })

  test('renders correctly when key changes from null to valid', async ({
    page
  }) => {
    await page.goto('./render-suspense-null-key', { waitUntil: 'commit' })

    const data = page.getByTestId('data')

    await expect(data).toHaveText('render-suspense-null-key-123')

    await page.getByTestId('set-empty').click()
    await expect(data).toHaveText('render-suspense-null-key-nodata')

    await page.getByTestId('set-456').click()
    await expect(data).toHaveText('render-suspense-null-key-456')
  })

  test('handles undefined key toggling', async ({ page }) => {
    await page.goto('./suspense-undefined-key', { waitUntil: 'commit' })

    await expect(page.getByText('empty')).toBeVisible()

    await page.getByRole('button', { name: 'toggle' }).click()

    await expect(page.getByText('fallback')).toBeVisible()
    await expect(page.getByText('SWR')).toBeVisible()
  })
})


================================================
FILE: e2e/test/suspense-undefined-key.test.ts
================================================
import { test, expect } from '@playwright/test'

test.describe('suspense with undefined key', () => {
  test('should render correctly when key is undefined', async ({ page }) => {
    await page.goto('./suspense-undefined-key', { waitUntil: 'commit' })

    // Should show content for undefined key (not suspense)
    await expect(page.getByText('empty')).toBeVisible()

    // Click toggle to enable key
    await page.getByRole('button', { name: 'toggle' }).click()

    // Should show loading fallback when key becomes defined
    await expect(page.getByText('fallback')).toBeVisible()

    // Should eventually show the fetched data
    await expect(page.getByText('SWR')).toBeVisible()
  })
})


================================================
FILE: e2e/test/tsconfig.json
================================================
{
  "extends": "../../tsconfig.json",
  "include": [
    ".",
  ],
}

================================================
FILE: eslint.config.mjs
================================================
import { defineConfig, globalIgnores } from 'eslint/config'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import reactHooks from 'eslint-plugin-react-hooks'
import globals from 'globals'
import tsParser from '@typescript-eslint/parser'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import js from '@eslint/js'
import { FlatCompat } from '@eslint/eslintrc'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const compat = new FlatCompat({
  baseDirectory: __dirname,
  recommendedConfig: js.configs.recommended,
  allConfig: js.configs.all
})

export default defineConfig([
  globalIgnores([
    '**/dist/',
    '**/node_modules',
    '**/scripts',
    '**/examples',
    '**/.next',
    '**/next.config.js',
    'eslint.config.mjs',
    'playwright.config.js',
    'jest.config.js',
    'jest.config.build.js',
    'test-result/**',
    'playwright-report/**'
  ]),
  {
    extends: compat.extends(
      'eslint:recommended',
      'plugin:react/recommended',
      'plugin:@typescript-eslint/recommended',
      'prettier',
      'plugin:jest-dom/recommended',
      'plugin:testing-library/react',
      'plugin:react/jsx-runtime'
    ),
    plugins: {
      '@typescript-eslint': typescriptEslint,
      'react-hooks': reactHooks
    },
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
        ...globals.jest
      },
      parser: tsParser,
      ecmaVersion: 8,
      sourceType: 'module',
      parserOptions: {
        ecmaFeatures: {
          impliedStrict: true,
          experimentalObjectRestSpread: true
        },
        allowImportExportEverywhere: true,
        project: ['**/tsconfig.json']
      }
    },
    settings: {
      react: {
        version: 'detect'
      }
    },
    rules: {
      'func-names': [2, 'as-needed'],
      'no-shadow': 0,
      '@typescript-eslint/no-shadow': 2,
      '@typescript-eslint/explicit-function-return-type': 0,
      '@typescript-eslint/no-unused-vars': [
        0,
        {
          argsIgnorePattern: '^_'
        }
      ],
      '@typescript-eslint/no-use-before-define': 0,
      '@typescript-eslint/ban-ts-ignore': 0,
      '@typescript-eslint/no-empty-function': 0,
      '@typescript-eslint/ban-ts-comment': 0,
      '@typescript-eslint/no-var-requires': 0,
      '@typescript-eslint/no-explicit-any': 0,
      '@typescript-eslint/explicit-module-boundary-types': 0,
      '@typescript-eslint/consistent-type-imports': [
        2,
        {
          prefer: 'type-imports'
        }
      ],
      '@typescript-eslint/ban-types': 0,
      'react-hooks/rules-of-hooks': 2,
      'react-hooks/exhaustive-deps': 1,
      'react/prop-types': 0,
      'testing-library/no-unnecessary-act': 0
    }
  },
  {
    files: ['e2e/**/*.ts'],
    rules: {
      'testing-library/prefer-screen-queries': 'off'
    }
  }
])


================================================
FILE: examples/.eslintrc
================================================
// next is loading eslintrc from the root directory, adding this to avoid eslint rules being overridden
{}


================================================
FILE: examples/api-hooks/README.md
================================================
# API Hooks

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/api-hooks)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/api-hooks
cd api-hooks
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how you could create custom hooks, using SWR internally, for your different data requirements and use them in your application.


================================================
FILE: examples/api-hooks/hooks/use-projects.js
================================================
import useSWR from 'swr'

import fetch from '../libs/fetch'

export default function useProjects() {
  return useSWR('/api/data', fetch)
}



================================================
FILE: examples/api-hooks/hooks/use-repository.js
================================================
import useSWR from 'swr'

import fetch from '../libs/fetch'

export default function useRepository(id) {
  return useSWR('/api/data?id=' + id, fetch)
}


================================================
FILE: examples/api-hooks/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/api-hooks/package.json
================================================
{
  "name": "api-hooks",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/api-hooks/pages/[user]/[repo].js
================================================
import Link from 'next/link'
import useRepository from '../../hooks/use-repository'

export default function Repo() {
  const id = typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
  const { data } = useRepository(id)

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      {
        data ? <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div> : 'loading...'
      }
      <br />
      <br />
      <Link href="/">Back</Link>
    </div>
  )
}


================================================
FILE: examples/api-hooks/pages/api/data.js
================================================
const projects = [
  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'
]

export default (req, res) => {
  if (req.query.id) {
    // a slow endpoint for getting repo data
    fetch(`https://api.github.com/repos/${req.query.id}`)
      .then(resp => resp.json())
      .then(data => {
        setTimeout(() => {
          res.json(data)
        }, 2000)
      })

    return
  }
  setTimeout(() => {
    res.json(projects)
  }, 2000)
}


================================================
FILE: examples/api-hooks/pages/index.js
================================================
import Link from 'next/link'
import useProjects from '../hooks/use-projects'

export default function Index() {
  const { data } = useProjects()

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <div>
      {
        data ? data.map(project =>
          <p key={project}><Link href='/[user]/[repo]' as={`/${project}`}>{project}</Link></p>
        ) : 'loading...'
      }
      </div>
    </div>
  )
}


================================================
FILE: examples/autocomplete-suggestions/README.md
================================================
# Autocomplete Suggestions

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/autocomplete-suggestions)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/autocomplete-suggestions
cd autocomplete-suggestions
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how to use SWR to fetch the suggestion for an autocomplete.


================================================
FILE: examples/autocomplete-suggestions/libs/fetcher.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/autocomplete-suggestions/package.json
================================================
{
  "name": "autocomplete-suggestions",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "@reach/combobox": "0.16.1",
    "lodash.debounce": "4.0.8",
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/autocomplete-suggestions/pages/api/suggestions.js
================================================
const countries = [
  "United States",
  "Canada",
  "United Kingdom",
  "Argentina",
  "Australia",
  "Austria",
  "Belgium",
  "Brazil",
  "Chile",
  "China",
  "Colombia",
  "Croatia",
  "Denmark",
  "Dominican Republic",
  "Egypt",
  "Finland",
  "France",
  "Germany",
  "Greece",
  "Hong Kong",
  "India",
  "Indonesia",
  "Ireland",
  "Israel",
  "Italy",
  "Japan",
  "Jordan",
  "Kuwait",
  "Lebanon",
  "Malaysia",
  "Mexico",
  "Netherlands",
  "New Zealand",
  "Nigeria",
  "Norway",
  "Pakistan",
  "Panama",
  "Peru",
  "Philippines",
  "Poland",
  "Russia",
  "Saudi Arabia",
  "Serbia",
  "Singapore",
  "South Africa",
  "South Korea",
  "Spain",
  "Sweden",
  "Switzerland",
  "Taiwan",
  "Thailand",
  "Turkey",
  "United Arab Emirates",
  "Venezuela",
  "Portugal",
  "Luxembourg",
  "Bulgaria",
  "Czech Republic",
  "Slovenia",
  "Iceland",
  "Slovakia",
  "Lithuania",
  "Trinidad and Tobago",
  "Bangladesh",
  "Sri Lanka",
  "Kenya",
  "Hungary",
  "Morocco",
  "Cyprus",
  "Jamaica",
  "Ecuador",
  "Romania",
  "Bolivia",
  "Guatemala",
  "Costa Rica",
  "Qatar",
  "El Salvador",
  "Honduras",
  "Nicaragua",
  "Paraguay",
  "Uruguay",
  "Puerto Rico",
  "Bosnia and Herzegovina",
  "Palestinian Authority",
  "Tunisia",
  "Bahrain",
  "Vietnam",
  "Ghana",
  "Mauritius",
  "Ukraine",
  "Malta",
  "Bahamas",
  "Maldives",
  "Oman",
  "Macedonia",
  "Latvia",
  "Estonia",
  "Iraq",
  "Algeria",
  "Albania",
  "Nepal",
  "Macau",
  "Montenegro",
  "Senegal",
  "Georgia",
  "Brunei",
  "Uganda",
  "Guadeloupe",
  "Barbados",
  "Azerbaijan",
  "Tanzania",
  "Libya",
  "Martinique",
  "Cameroon",
  "Botswana",
  "Ethiopia",
  "Kazakhstan",
  "Namibia",
  "Netherlands Antilles",
  "Madagascar",
  "New Caledonia",
  "Moldova",
  "Fiji",
  "Belarus",
  "Jersey",
  "Guam",
  "Yemen",
  "Zambia",
  "Isle Of Man",
  "Haiti",
  "Cambodia",
  "Aruba",
  "French Polynesia",
  "Afghanistan",
  "Bermuda",
  "Guyana",
  "Armenia",
  "Malawi",
  "Antigua and Barbuda",
  "Rwanda",
  "Guernsey",
  "Gambia",
  "Faroe Islands",
  "St. Lucia",
  "Cayman Islands",
  "Benin",
  "Andorra",
  "Grenada",
  "US Virgin Islands",
  "Belize",
  "Saint Vincent and the Grenadines",
  "Mongolia",
  "Mozambique",
  "Mali",
  "Angola",
  "French Guiana",
  "Uzbekistan",
  "Djibouti",
  "Burkina Faso",
  "Monaco",
  "Togo",
  "Greenland",
  "Gabon",
  "Gibraltar",
  "Republic of the Congo",
  "Kyrgyzstan",
  "Papua New Guinea",
  "Bhutan",
  "Saint Kitts and Nevis",
  "Swaziland",
  "Lesotho",
  "Laos",
  "Liechtenstein",
  "Northern Mariana Islands",
  "Suriname",
  "Seychelles",
  "British Virgin Islands",
  "Turks and Caicos Islands",
  "Dominica",
  "Mauritania",
  "Aland Islands",
  "San Marino",
  "Sierra Leone",
  "Niger",
  "Democratic Republic of the Congo",
  "Anguilla",
  "Mayotte",
  "Cape Verde",
  "Guinea",
  "Turkmenistan",
  "Burundi",
  "Tajikistan",
  "Vanuatu",
  "Solomon Islands",
  "Eritrea",
  "Samoa",
  "American Samoa",
  "Falkland Islands",
  "Equatorial Guinea",
  "Tonga",
  "Comoros",
  "Palau",
  "Federated States of Micronesia",
  "Central African Republic",
  "Somalia",
  "Marshall Islands",
  "Vatican City",
  "Chad",
  "Kiribati",
  "Sao Tome and Principe",
  "Tuvalu",
  "Nauru",
  "Réunion",
  "Cuba",
  "Cote d'Ivoire",
  "Timor Leste",
  "North Korea",
  "Iran",
  "Liberia",
  "Myanmar",
  "Oman",
  "Saint Lucia",
  "Syria",
  "Sudan"
]

export default function suggestions(req, res) {
  const results = countries
    .filter(country => country.toLowerCase().startsWith(req.query.value))

  res.json(results);
}


================================================
FILE: examples/autocomplete-suggestions/pages/index.js
================================================
import { useMemo, useState } from "react"
import fetcher from '../libs/fetcher'
import {
  Combobox,
  ComboboxInput,
  ComboboxPopover,
  ComboboxList,
  ComboboxOption
} from '@reach/combobox'
import debounce from 'lodash.debounce'

import useSWR from 'swr'

export default function Index() {
  const [searchTerm, setSearchTerm] = useState(null)
  const { data: countries = [], isValidating } = useSWR(
    () => (searchTerm ? `/api/suggestions?value=${searchTerm}` : null),
    fetcher
  )

  function handleChange(event) {
    setSearchTerm(event.target.value)
  }

  const debouncedHandleChange = useMemo(
    () => debounce(handleChange, 500)
  , []);

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Country Search</h1>
      <Combobox>
        <ComboboxInput
          className="country-search-input"
          onChange={debouncedHandleChange}
          aria-label="Countries"
        />
        {countries && countries.length > 0 && (
          <ComboboxPopover className="shadow-popup">
            <ComboboxList>
              {countries.map(country => (
                <ComboboxOption key={country} value={country} />
              ))}
            </ComboboxList>
          </ComboboxPopover>
        )}
      </Combobox>
    </div>
  )
}


================================================
FILE: examples/axios/README.md
================================================
# Axios

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/axios)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/axios
cd axios
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show a basic usage of SWR fetching using axios and a request object.


================================================
FILE: examples/axios/libs/useRequest.js
================================================
import useSWR from 'swr'
import axios from 'axios'

export default function useRequest(request, { fallbackData, ...config } = {}) {
  return useSWR(
    request,
    () => axios(request || {}).then(response => response.data),
    {
      ...config,
      fallbackData: fallbackData && {
        status: 200,
        statusText: 'InitialData',
        headers: {},
        data: fallbackData
      }
    }
  ) 
}


================================================
FILE: examples/axios/package.json
================================================
{
  "name": "basic",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "axios": "0.27.2",
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/axios/pages/[user]/[repo].js
================================================
import Link from 'next/link'

import useRequest from '../../libs/useRequest'

export default function Repo() {
  const id =
    typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
  const { data } = useRequest(
    id
      ? {
          url: '/api/data',
          params: {
            id
          }
        }
      : null
  )

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      {data ? (
        <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div>
      ) : (
        'loading...'
      )}
      <br />
      <br />
      <Link href="/">
        Back
      </Link>
    </div>
  )
}


================================================
FILE: examples/axios/pages/api/data.js
================================================
import axios from 'axios'

const projects = [
  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'
]

export default function api(req, res) {
  if (req.query.id) {
    // a slow endpoint for getting repo data
    axios(`https://api.github.com/repos/${req.query.id}`)
    .then(resp => resp.data)
    .then(data => {
      setTimeout(() => {
        res.json(data)
      }, 2000)
    })
    return
  }
  setTimeout(() => {
    res.json(projects)
  }, 2000)
}


================================================
FILE: examples/axios/pages/index.js
================================================
import Link from 'next/link'

import useRequest from '../libs/useRequest'

export default function Index() {
  const { data } = useRequest({
    url: '/api/data'
  })

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <div>
        {data
          ? data.map(project => (
              <p key={project}>
                <Link href="/[user]/[repo]" as={`/${project}`}>
                  {project}
                </Link>
              </p>
            ))
          : 'loading...'}
      </div>
    </div>
  )
}


================================================
FILE: examples/axios-typescript/README.md
================================================
# Axios TypeScript

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/axios-typescript)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/axios-typescript
cd axios-typescript
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how to use the basic axios along with TypeScript to type both the request object and the data received from SWR.


================================================
FILE: examples/axios-typescript/libs/useRequest.ts
================================================
import useSWR, { SWRConfiguration, SWRResponse } from 'swr'
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'

export type GetRequest = AxiosRequestConfig | null

interface Return<Data, Error>
  extends Pick<
    SWRResponse<AxiosResponse<Data>, AxiosError<Error>>,
    'isValidating' | 'error' | 'mutate'
  > {
  data: Data | undefined
  response: AxiosResponse<Data> | undefined
}

export interface Config<Data = unknown, Error = unknown>
  extends Omit<
    SWRConfiguration<AxiosResponse<Data>, AxiosError<Error>>,
    'fallbackData'
  > {
  fallbackData?: Data
}

export default function useRequest<Data = unknown, Error = unknown>(
  request: GetRequest,
  { fallbackData, ...config }: Config<Data, Error> = {}
): Return<Data, Error> {
  const {
    data: response,
    error,
    isValidating,
    mutate
  } = useSWR<AxiosResponse<Data>, AxiosError<Error>>(
    request,
    /**
     * NOTE: Typescript thinks `request` can be `null` here, but the fetcher
     * function is actually only called by `useSWR` when it isn't.
     */
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    () => axios.request<Data>(request!),
    {
      ...config,
      fallbackData: fallbackData && {
        status: 200,
        statusText: 'InitialData',
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        config: request!,
        headers: {},
        data: fallbackData
      } as AxiosResponse<Data>
    }
  )

  return {
    data: response && response.data,
    response,
    error,
    isValidating,
    mutate
  }
}


================================================
FILE: examples/axios-typescript/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.


================================================
FILE: examples/axios-typescript/package.json
================================================
{
  "name": "basic-typescript",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "axios": "0.23.0",
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  },
  "devDependencies": {
    "@types/node": "16.7.2",
    "@types/react": "17.0.19",
    "typescript": "4.3.5"
  }
}


================================================
FILE: examples/axios-typescript/pages/[user]/[repo].tsx
================================================
import Link from 'next/link'

import useRequest from '../../libs/useRequest'

export default function Repo() {
  const id =
    typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
  const { data } = useRequest<{
    forks_count: number
    stargazers_count: number
    watchers: number
  }>({
    url: '/api/data',
    params: { id }
  })

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      {data ? (
        <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div>
      ) : (
        'loading...'
      )}
      <br />
      <br />
      <Link href="/">Back</Link>
    </div>
  )
}


================================================
FILE: examples/axios-typescript/pages/api/data.ts
================================================
import { NextApiRequest, NextApiResponse } from 'next'
import axios from 'axios'

const projects = [
  'facebook/flipper',
  'vuejs/vuepress',
  'rust-lang/rust',
  'vercel/next.js'
]

export default function api(req: NextApiRequest, res: NextApiResponse) {
  if (req.query.id) {
    // a slow endpoint for getting repo data
    axios(`https://api.github.com/repos/${req.query.id}`)
      .then(response => response.data)
      .then(data => {
        setTimeout(() => {
          res.json(data)
        }, 2000)
      })

    return
  }
  setTimeout(() => {
    res.json(projects)
  }, 2000)
}


================================================
FILE: examples/axios-typescript/pages/index.tsx
================================================
import Link from 'next/link'

import useRequest from '../libs/useRequest'

export default function Index() {
  const { data } = useRequest<string[]>({
    url: '/api/data'
  })

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <div>
        {data
          ? data.map(project => (
              <p key={project}>
                <Link href="/[user]/[repo]" as={`/${project}`}>
                  {project}
                </Link>
              </p>
            ))
          : 'loading...'}
      </div>
    </div>
  )
}


================================================
FILE: examples/axios-typescript/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ]
}


================================================
FILE: examples/basic/README.md
================================================
# Basic

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/basic)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/basic
cd basic
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show a basic usage of SWR fetching data from an API in two different pages.


================================================
FILE: examples/basic/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/basic/package.json
================================================
{
  "name": "basic",
  "private": true,
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/basic/pages/[user]/[repo].js
================================================
import Link from 'next/link'
import fetch from '../../libs/fetch'

import useSWR from 'swr'

export default function Repo() {
  const id = typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
  const { data } = useSWR('/api/data?id=' + id, fetch)

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      {
        data ? <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div> : 'loading...'
      }
      <br />
      <br />
      <Link href="/">Back</Link>
    </div>
  )
}


================================================
FILE: examples/basic/pages/api/data.js
================================================
const projects = [
  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'
]

export default function api(req, res) {
  if (req.query.id) {
    // a slow endpoint for getting repo data
    fetch(`https://api.github.com/repos/${req.query.id}`)
      .then(resp => resp.json())
      .then(data => {
        setTimeout(() => {
          res.json(data)
        }, 2000)
      })

    return
  }
  setTimeout(() => {
    res.json(projects)
  }, 2000)
}


================================================
FILE: examples/basic/pages/index.js
================================================
import Link from 'next/link'
import fetch from '../libs/fetch'

import useSWR from 'swr'

export default function Index() {
  const { data } = useSWR('/api/data', fetch)

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <div>
      {
        data ? data.map(project =>
          <p key={project}><Link href='/[user]/[repo]' as={`/${project}`}>{project}</Link></p>
        ) : 'loading...'
      }
      </div>
    </div>
  )
}


================================================
FILE: examples/basic-typescript/README.md
================================================
# Basic TypeScript

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/basic-typescript)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/basic-typescript
cd basic-typescript
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how to use the basic example along with TypeScript to type the data received from SWR.


================================================
FILE: examples/basic-typescript/libs/fetch.ts
================================================
export default async function fetcher<JSON = any>(
  input: RequestInfo,
  init?: RequestInit
): Promise<JSON> {
  const res = await fetch(input, init)
  return res.json()
}


================================================
FILE: examples/basic-typescript/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.


================================================
FILE: examples/basic-typescript/package.json
================================================
{
  "name": "basic-typescript",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  },
  "devDependencies": {
    "@types/node": "16.7.2",
    "@types/react": "17.0.19",
    "typescript": "4.3.5"
  }
}


================================================
FILE: examples/basic-typescript/pages/[user]/[repo].tsx
================================================
import Link from 'next/link'
import fetch from '../../libs/fetch'

import useSWR from 'swr'

export default function Repo() {
  const id =
    typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
  const { data } = useSWR<{
    forks_count: number
    stargazers_count: number
    watchers: number
  }>('/api/data?id=' + id, fetch)

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      {data ? (
        <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div>
      ) : (
        'loading...'
      )}
      <br />
      <br />
      <Link href="/">Back</Link>
    </div>
  )
}


================================================
FILE: examples/basic-typescript/pages/api/data.ts
================================================
import { NextApiRequest, NextApiResponse } from 'next'

const projects = [
  'facebook/flipper',
  'vuejs/vuepress',
  'rust-lang/rust',
  'vercel/next.js'
]

export default function api(req: NextApiRequest, res: NextApiResponse) {
  if (req.query.id) {
    // a slow endpoint for getting repo data
    fetch(`https://api.github.com/repos/${req.query.id}`)
      .then(resp => resp.json())
      .then(data => {
        setTimeout(() => {
          res.json(data)
        }, 2000)
      })

    return
  }
  setTimeout(() => {
    res.json(projects)
  }, 2000)
}


================================================
FILE: examples/basic-typescript/pages/index.tsx
================================================
import Link from 'next/link'
import fetch from '../libs/fetch'

import useSWR from 'swr'

export default function HomePage() {
  const { data } = useSWR<string[]>('/api/data', fetch)
  const { data: data2 } = useSWR(null, fetch)

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      {data2}
      <div>
        {data
          ? data.map(project => (
              <p key={project}>
                <Link href="/[user]/[repo]" as={`/${project}`}>
                  {project}
                </Link>
              </p>
            ))
          : 'loading...'}
      </div>
    </div>
  )
}


================================================
FILE: examples/basic-typescript/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ]
}


================================================
FILE: examples/focus-revalidate/README.md
================================================
# Focus Revalidate

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/focus-revalidate)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/focus-revalidate
cd focus-revalidate
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Basic authentication example showing how the revalidate on focus feature works and to trigger a revalidation on a per-hook call basis.


================================================
FILE: examples/focus-revalidate/components/button.js
================================================
export default function Button({ children, ...props }) {
  return <div><button {...props}>
    {children}
    <style jsx>{`
      button {
        width: 100px;
        height: 40px;
        border: none;
        color: #fff;
        background: #03a9f4;
        font-size: 1rem;
        border-radius: 5px;
      }
    `}</style>
  </button></div>
}

================================================
FILE: examples/focus-revalidate/libs/auth.js
================================================
// mock login and logout

export function login() {
  document.cookie = 'swr-test-token=swr;'
}

export function logout() {
  document.cookie = 'swr-test-token=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'
}


================================================
FILE: examples/focus-revalidate/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/focus-revalidate/package.json
================================================
{
  "name": "focus-revalidate",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/focus-revalidate/pages/api/user.js
================================================
// an endpoint for getting user info
export default function user(req, res) {
  if (req.cookies['swr-test-token'] === 'swr') {
    // authorized
    res.json({
      loggedIn: true,
      name: 'Shu',
      avatar: 'https://github.com/shuding.png'
    })
    return
  }

  res.json({
    loggedIn: false
  })
}


================================================
FILE: examples/focus-revalidate/pages/index.js
================================================
import Button from '../components/button'
import fetch from '../libs/fetch'
import { login, logout } from '../libs/auth'

import useSWR from 'swr'

export default function Index() {
  const { data, mutate } = useSWR('/api/user', fetch)

  if (!data) return <h1>loading...</h1>
  if (data.loggedIn) {
    return <div>
      <h1>Welcome, {data.name}</h1>
      <img src={data.avatar} width={80} />
      <Button onClick={() => {
        logout()
        mutate() // after logging in/out, we mutate the SWR
      }}>Logout</Button>
    </div>
  } else {
    return <div>
      <h1>Please login</h1>
      <Button onClick={() => {
        login()
        mutate()
      }}>Login</Button>
    </div>
  }
}


================================================
FILE: examples/global-fetcher/README.md
================================================
# Global Fetcher

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/global-fetcher)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/global-fetcher
cd global-fetcher
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Use the `SWRConfig` provider to set up the fetcher globally instead of a per-hook call.


================================================
FILE: examples/global-fetcher/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/global-fetcher/package.json
================================================
{
  "name": "global-fetcher",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/global-fetcher/pages/[user]/[repo].js
================================================
import Link from 'next/link'

import useSWR from 'swr'

export default function Repo() {
  const id = typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
  const { data } = useSWR('/api/data?id=' + id)

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      {
        data ? <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div> : 'loading...'
      }
      <br />
      <br />
      <Link href="/">Back</Link>
    </div>
  )
}


================================================
FILE: examples/global-fetcher/pages/_app.js
================================================
import React from 'react'
import App from 'next/app'
import { SWRConfig } from 'swr'
import fetch from '../libs/fetch.js';

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props
    return <SWRConfig
      value={{
        fetcher: fetch
      }}
    >
      <Component {...pageProps} />
    </SWRConfig>
  }
}


================================================
FILE: examples/global-fetcher/pages/api/data.js
================================================
const projects = [
  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'
]

export default function api(req, res) {
  if (req.query.id) {
    // a slow endpoint for getting repo data
    fetch(`https://api.github.com/repos/${req.query.id}`)
      .then(resp => resp.json())
      .then(data => {
        setTimeout(() => {
          res.json(data)
        }, 2000)
      })

    return
  }
  setTimeout(() => {
    res.json(projects)
  }, 2000)
}


================================================
FILE: examples/global-fetcher/pages/index.js
================================================
import Link from 'next/link'

import useSWR from 'swr'

export default function Index() {
  const { data } = useSWR('/api/data')

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <div>
        {data
          ? data.map(project => (
              <p key={project}>
                <Link href="/[user]/[repo]" as={`/${project}`}>
                  {project}
                </Link>
              </p>
            ))
          : 'loading...'}
      </div>
    </div>
  )
}


================================================
FILE: examples/infinite/README.md
================================================
# useSWRInfinite

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/infinite)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/infinite
cd basic
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show usage of useSWRInfinite with load more data button


================================================
FILE: examples/infinite/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/infinite/package.json
================================================
{
  "name": "infinite",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/infinite/pages/index.js
================================================
import { useState } from 'react'
import useSWRInfinite from 'swr/infinite'

import fetch from '../libs/fetch'

const PAGE_SIZE = 6

export default function App() {
  const [repo, setRepo] = useState('reactjs/react-a11y')
  const [val, setVal] = useState(repo)

  const { data, error, mutate, size, setSize, isValidating } = useSWRInfinite(
    (index) =>
      `https://api.github.com/repos/${repo}/issues?per_page=${PAGE_SIZE}&page=${
        index + 1
      }`,
    fetch
  )

  const issues = data ? [].concat(...data) : []
  const isLoadingInitialData = !data && !error
  const isLoadingMore =
    isLoadingInitialData ||
    (size > 0 && data && typeof data[size - 1] === 'undefined')
  const isEmpty = data?.[0]?.length === 0
  const isReachingEnd =
    isEmpty || (data && data[data.length - 1]?.length < PAGE_SIZE)
  const isRefreshing = isValidating && data && data.length === size

  return (
    <div style={{ fontFamily: 'sans-serif' }}>
      <input
        value={val}
        onChange={(e) => setVal(e.target.value)}
        placeholder="reactjs/react-a11y"
      />
      <button
        onClick={() => {
          setRepo(val)
          setSize(1)
        }}
      >
        load issues
      </button>
      <p>
        showing {size} page(s) of {isLoadingMore ? '...' : issues.length}{' '}
        issue(s){' '}
        <button
          disabled={isLoadingMore || isReachingEnd}
          onClick={() => setSize(size + 1)}
        >
          {isLoadingMore
            ? 'loading...'
            : isReachingEnd
            ? 'no more issues'
            : 'load more'}
        </button>
        <button disabled={isRefreshing} onClick={() => mutate()}>
          {isRefreshing ? 'refreshing...' : 'refresh'}
        </button>
        <button disabled={!size} onClick={() => setSize(0)}>
          clear
        </button>
      </p>
      {isEmpty ? <p>Yay, no issues found.</p> : null}
      {issues.map((issue) => {
        return (
          <p key={issue.id} style={{ margin: '6px 0' }}>
            - {issue.title}
          </p>
        )
      })}
    </div>
  )
}


================================================
FILE: examples/infinite-scroll/README.md
================================================
# useSWRInfinite with scroll based on IntersectionObserver

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/infinite-scroll)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/infinite-scroll
cd infinite-scroll
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show usage of useSWRInfinite with infinite scroll based on IntersectionObserver


================================================
FILE: examples/infinite-scroll/hooks/useOnScreen.js
================================================
import { useState, useEffect } from 'react'

export default function useOnScreen(ref) {
  const [isIntersecting, setIntersecting] = useState(false)

  useEffect(() => {
    if (!ref.current) return

    const observer = new IntersectionObserver(([entry]) =>
      setIntersecting(entry.isIntersecting)
    )

    observer.observe(ref.current)
    // Remove the observer as soon as the component is unmounted
    return () => {
      observer.disconnect()
    }
  }, [])

  return isIntersecting
}


================================================
FILE: examples/infinite-scroll/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/infinite-scroll/package.json
================================================
{
  "name": "infinite-scroll",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/infinite-scroll/pages/index.js
================================================
import useSWRInfinite from 'swr/infinite'
import { useState, useRef, useEffect } from 'react'

import fetcher from '../libs/fetch'
import useOnScreen from '../hooks/useOnScreen'

const PAGE_SIZE = 6

const getKey = (pageIndex, previousPageData, repo, pageSize) => {
  if (previousPageData && !previousPageData.length) return null // reached the end

  return `https://api.github.com/repos/${repo}/issues?per_page=${pageSize}&page=${
    pageIndex + 1
  }`
}

export default function App() {
  const ref = useRef()
  const [repo, setRepo] = useState('facebook/react')
  const [val, setVal] = useState(repo)

  const isVisible = useOnScreen(ref)

  const { data, error, mutate, size, setSize, isValidating } = useSWRInfinite(
    (...args) => getKey(...args, repo, PAGE_SIZE),
    fetcher
  )

  const issues = data ? [].concat(...data) : []
  const isLoadingInitialData = !data && !error
  const isLoadingMore =
    isLoadingInitialData ||
    (size > 0 && data && typeof data[size - 1] === 'undefined')
  const isEmpty = data?.[0]?.length === 0
  const isReachingEnd = size === PAGE_SIZE
  const isRefreshing = isValidating && data && data.length === size

  useEffect(() => {
    if (isVisible && !isReachingEnd && !isRefreshing) {
      setSize(size + 1)
    }
  }, [isVisible, isRefreshing])

  return (
    <div style={{ fontFamily: 'sans-serif' }}>
      <input
        value={val}
        onChange={(e) => setVal(e.target.value)}
        placeholder="facebook/react"
      />
      <button
        onClick={() => {
          setRepo(val)
          setSize(1)
        }}
      >
        load issues
      </button>
      <p>
        showing {size} page(s) of {isLoadingMore ? '...' : issues.length}{' '}
        issue(s){' '}
        <button disabled={isRefreshing} onClick={() => mutate()}>
          {isRefreshing ? 'refreshing...' : 'refresh'}
        </button>
        <button disabled={!size} onClick={() => setSize(0)}>
          clear
        </button>
      </p>
      {isEmpty ? <p>Yay, no issues found.</p> : null}
      {issues.map((issue) => {
        return (
          <p key={issue.id} style={{ margin: '6px 0', height: 50 }}>
            - {issue.title}
          </p>
        )
      })}
      <div ref={ref}>
        {isLoadingMore ? 'loading...' : isReachingEnd ? 'no more issues' : ''}
      </div>
    </div>
  )
}


================================================
FILE: examples/local-state-sharing/README.md
================================================
# Basic

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/local-state-sharing)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/local-state-sharing
cd local-state-sharing
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how to share local state between React components using SWR.


================================================
FILE: examples/local-state-sharing/libs/store.js
================================================
const initialStore = { name: "john" };

export default initialStore;


================================================
FILE: examples/local-state-sharing/package.json
================================================
{
  "name": "local-state-sharing",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "react": "latest",
    "react-dom": "latest",
    "next": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/local-state-sharing/pages/index.js
================================================
import { useState } from "react"
import initialStore from "../libs/store"
import useSWR, { mutate } from "swr"

function Profile() {
  const { data } = useSWR("globalState", { fallbackData: initialStore })
  const [value, updateValue] = useState((data || {}).name)
  if (!data) {
    return null
  }
  return (
    <div>
      <h1>My name is {data.name}.</h1>
      <input
        value={value}
        onChange={e => updateValue(e.target.value)}
        style={{ width: 200, marginRight: 8 }}
      />
      <button
        type="button"
        onClick={() => {
          mutate("globalState", { ...data, name: value }, false)
        }}
      >
        Uppercase my name!
      </button>
    </div>
  )
}

function Other() {
  const { data } = useSWR("globalState", { fallbackData: initialStore })
  if (!data) {
    return null
  }
  return (
    <div style={{ border: "1px solid #ddd", marginTop: 30, padding: 20 }}>
      <h1>
        Another Component: <br />
        My name is {data.name}.
      </h1>
    </div>
  )
}

export default function Index() {
  return (
    <div style={{ padding: 40 }}>
      useSWR can share state between components:
      <Profile />
      <Other />
    </div>
  )
}


================================================
FILE: examples/optimistic-ui/README.md
================================================
# Optimistic UI

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/optimistic-ui)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/optimistic-ui
cd optimistic-ui
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Example of how to use SWR to implement an Optimistic UI pattern where we mutate the cached data immediately and then trigger a revalidation with the API.


================================================
FILE: examples/optimistic-ui/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  if (!res.ok) throw new Error('Failed to fetch')
  return res.json()
}


================================================
FILE: examples/optimistic-ui/package.json
================================================
{
  "name": "optimistic-ui",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/optimistic-ui/pages/_app.js
================================================
import '../styles.css'

export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />
}


================================================
FILE: examples/optimistic-ui/pages/api/todos.js
================================================
let todos = []
const delay = () => new Promise(res => setTimeout(() => res(), 1000))

async function getTodos() {
  await delay()
  return todos.sort((a, b) => (a.text < b.text ? -1 : 1))
}

async function addTodo(todo) {
  await delay()
  // Sometimes it will fail, this will cause a regression on the UI
  if (Math.random() < 0.2 || !todo.text)
    throw new Error('Failed to add new item!')
  todo.text = todo.text.charAt(0).toUpperCase() + todo.text.slice(1)
  todos = [...todos, todo]
  return todo
}

export default async function api(req, res) {
  try {
    if (req.method === 'POST') {
      const body = JSON.parse(req.body)
      return res.json(await addTodo(body))
    }

    return res.json(await getTodos())
  } catch (err) {
    return res.status(500).json({ error: err.message })
  }
}


================================================
FILE: examples/optimistic-ui/pages/index.js
================================================
import useSWR from 'swr'
import React, { useState } from 'react'

import fetch from '../libs/fetch'

export default function App() {
  const [text, setText] = useState('')
  const { data, mutate } = useSWR('/api/todos', fetch)

  const [state, setState] = useState(<span className="info">&nbsp;</span>)

  return (
    <div>
      {/* <Toaster toastOptions={{ position: 'bottom-center' }} /> */}
      <h1>Optimistic UI with SWR</h1>

      <p className="note">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
          <path d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm1 18h-2v-8h2v8zm-1-12.25c.69 0 1.25.56 1.25 1.25s-.56 1.25-1.25 1.25-1.25-.56-1.25-1.25.56-1.25 1.25-1.25z" />
        </svg>
        This application optimistically updates the data, while revalidating in
        the background. The <code>POST</code> API auto capitializes the data,
        and only returns the new added one instead of the full list. And the{' '}
        <code>GET</code> API returns the full list in order.
      </p>

      <form onSubmit={ev => ev.preventDefault()}>
        <input
          value={text}
          onChange={e => setText(e.target.value)}
          placeholder="Add your to-do here..."
          autoFocus
        />
        <button
          type="submit"
          onClick={async () => {
            setText('')
            setState(
              <span className="info">Showing optimistic data, mutating...</span>
            )

            const newTodo = {
              id: Date.now(),
              text
            }

            try {
              // Update the local state immediately and fire the
              // request. Since the API will return the updated
              // data, there is no need to start a new revalidation
              // and we can directly populate the cache.
              await mutate(
                fetch('/api/todos', {
                  method: 'POST',
                  body: JSON.stringify(newTodo)
                }),
                {
                  optimisticData: [...data, newTodo],
                  rollbackOnError: true,
                  populateCache: newItem => {
                    setState(
                      <span className="success">
                        Successfully mutated the resource and populated cache.
                        Revalidating...
                      </span>
                    )

                    return [...data, newItem]
                  },
                  revalidate: true
                }
              )
              setState(<span className="info">Revalidated the resource.</span>)
            } catch (e) {
              // If the API errors, the original data will be
              // rolled back by SWR automatically.
              setState(
                <span className="error">
                  Failed to mutate. Rolled back to previous state and
                  revalidated the resource.
                </span>
              )
            }
          }}
        >
          Add
        </button>
      </form>

      {state}

      <ul>
        {data ? (
          data.length ? (
            data.map(todo => {
              return <li key={todo.id}>{todo.text}</li>
            })
          ) : (
            <i>
              No todos yet. Try adding lowercased "banana" and "apple" to the
              list.
            </i>
          )
        ) : (
          <i>Loading...</i>
        )}
      </ul>
    </div>
  )
}


================================================
FILE: examples/optimistic-ui/styles.css
================================================
html {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  text-align: center;
}

body {
  max-width: 600px;
  margin: auto;
}

h1 {
  margin-top: 1em;
}

.note {
  text-align: left;
  font-size: 0.9em;
  line-height: 1.5;
  color: #666;
}

.note svg {
  margin-right: 0.5em;
  vertical-align: -2px;
  width: 14px;
  height: 14px;
  margin-right: 5px;
}

form {
  display: flex;
  margin: 8px 0;
  gap: 8px;
}

input {
  flex: 1;
}

input,
button {
  font-size: 16px;
  padding: 6px 5px;
}

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
    Liberation Mono, Courier New, monospace;
  font-feature-settings: 'rlig' 1, 'calt' 1, 'ss01' 1;
  background-color: #eee;
  padding: 1px 3px;
  border-radius: 2px;
}

ul {
  text-align: left;
  list-style: none;
  padding: 0;
}

li {
  margin: 8px 0;
  padding: 10px;
  border-radius: 4px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12), 0 0 0 1px #ededed;
}

i {
  color: #999;
}

.info,
.success,
.error {
  display: block;
  text-align: left;
  padding: 6px 0;
  font-size: 0.9em;
  opacity: 0.9;
}

.info {
  color: #666;
}
.success {
  color: #4caf50;
}
.error {
  color: #f44336;
}


================================================
FILE: examples/optimistic-ui-immer/README.md
================================================
# Optimistic UI with Immer

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/optimistic-ui-immer)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/optimistic-ui-immer
cd optimistic-ui-immer
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Example of how to use SWR and Immer to implement an Optimistic UI pattern where we mutate the cached data immediately and then trigger a revalidation with the API.


================================================
FILE: examples/optimistic-ui-immer/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/optimistic-ui-immer/package.json
================================================
{
  "name": "optimistic-ui-immer",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "immer": "9.0.5",
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/optimistic-ui-immer/pages/api/data.js
================================================
const data = []

function shouldFail() {
  return Math.random() > 0.8
}

export default function api(req, res) {
  if (req.method === 'POST') {
    const body = JSON.parse(req.body)
    // sometimes it will fail, and this will cause a regression in the UI
    if (!shouldFail()) {
      data.push(body.text);
    }
    res.json(data)
    return
  }

  setTimeout(() => {
    res.json(data)
  }, 2000)
}



================================================
FILE: examples/optimistic-ui-immer/pages/index.js
================================================
import React from 'react'
import fetch from '../libs/fetch'

import useSWR, { mutate } from 'swr'
import produce from "immer"

export default function Index() {
  const [text, setText] = React.useState('');
  const { data } = useSWR('/api/data', fetch)

  async function handleSubmit(event) {
    event.preventDefault()
    // Call mutate to optimistically update the UI.
    // We use Immer produce to allow us to perform an immutable change
    // while coding it as a normal mutation of the same object.
    mutate("/api/data", produce(draftData => {
      draftData.push(text)
    }), false)
    // Then we send the request to the API and let mutate
    // update the data with the API response.
    // Our action may fail in the API function, and the response differ
    // from what was optimistically updated, in that case, the UI will be
    // changed to match the API response.
    // The fetch could also fail, in that case, the UI will
    // be in an incorrect state until the next successful fetch.
    mutate('/api/data', await fetch('/api/data', {
      method: 'POST',
      body: JSON.stringify({ text })
    }))
    setText('')
  }

  return <div>
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        onChange={event => setText(event.target.value)}
        value={text}
      />
      <button>Create</button>
    </form>
    <ul>
      {data ? data.map(datum => <li key={datum}>{datum}</li>) : 'loading...'}
    </ul>
  </div>
}


================================================
FILE: examples/prefetch-preload/README.md
================================================
# Prefetch & Preload

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/prefetch-preload)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/prefetch-preload
cd prefetch-preload
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

This example shows multiple ways to prefetch data to be used by SWR later.

- Use a `<link preload>` to get the browser to load the data while rendering the HTML
- If in a browser, run the fetch + mutate outside the component
- After rendering use an effect in React to prefetch the next page's data
- When the user moves the mouse over a link trigger a fetch + mutate for the next page

In the real world you would not necessarily use all of them at the same time but one or more combined to give the best UX possible.


================================================
FILE: examples/prefetch-preload/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/prefetch-preload/package.json
================================================
{
  "name": "prefetch-preload",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/prefetch-preload/pages/[user]/[repo].js
================================================
import Head from "next/head"
import Link from 'next/link'
import React from 'react'
import fetch from '../../libs/fetch'

import useSWR, { mutate } from 'swr'

function prefetchParent() {
  return fetch('/api/data')
    .then(projects => mutate('/api/data', projects, false))
}

// if we are on the browser trigger a prefetch as soon as possible
if (typeof window !== 'undefined') prefetchParent()

export default function Repo() {
  const id = typeof window !== 'undefined' ? window.location.pathname.slice(1) : ''
  const { data } = useSWR('/api/data?id=' + id, fetch)

  React.useEffect(() => {
    prefetchParent()
  }, [])

  function handleMouseEnter() {
    prefetchParent()
  }

  return (
    <>
      <Head>
        {/* This will tell the browser to preload the data for our page */}
        {id && <link preload={`/api/data?id=${id}`} as="fetch" crossOrigin="anonymous" />}
      </Head>
      <div style={{ textAlign: 'center' }}>
        <h1>{id}</h1>
        {
          data ? <div>
            <p>forks: {data.forks_count}</p>
            <p>stars: {data.stargazers_count}</p>
            <p>watchers: {data.watchers}</p>
          </div> : 'loading...'
        }
        <br />
        <br />
        <Link href="/" onMouseEnter={handleMouseEnter}>Back</Link>
      </div>
    </>
  )
}


================================================
FILE: examples/prefetch-preload/pages/api/data.js
================================================
const projects = [
  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'
]

export default function api(req, res) {
  if (req.query.id) {
    // a slow endpoint for getting repo data
    fetch(`https://api.github.com/repos/${req.query.id}`)
      .then(resp => resp.json())
      .then(data => {
        setTimeout(() => {
          res.json(data)
        }, 2000)
      })

    return
  }
  setTimeout(() => {
    res.json(projects)
  }, 2000)
}


================================================
FILE: examples/prefetch-preload/pages/index.js
================================================
import React from 'react'
import Head from "next/head";
import Link from 'next/link'
import fetch from '../libs/fetch'

import useSWR, { mutate } from 'swr'

function prefetchData() {
  return fetch('/api/data')
    .then(data => {
      mutate('/api/data', data, false)
      return data
    })
}

function prefetchItem(project) {
  return fetch(`https://api.github.com/repos/${project}`).then(data => {
    mutate(`/api/data?id=${project}`, data, false)
    return data
  })
}

function prefetchWithProjects() {
  return prefetchData()
    .then(projects => Promise.all(projects.map(prefetchItem)))
}

// if we are on the browser trigger a prefetch as soon as possible
if (typeof window !== 'undefined') prefetchWithProjects()

export default function Index() {
  const { data } = useSWR('/api/data', fetch)

  // This effect will fetch all projects after mounting
  React.useEffect(() => {
    if (!data) return
    if (data.length === 0) return
    data.forEach(prefetchItem)
  }, [data]);

  // With this handler, you could prefetch the data for a specific
  // project the moment the user moves the mouse over the link
  function handleMouseEnter(event) {
    // In our case, we could get the ID from the href so we use that
    prefetchItem(event.target.getAttribute("href").slice(1))
  }

  return (
    <>
      <Head>
        {/* This will tell the browser to preload the data for our page */}
        <link preload="/api/data" as="fetch" crossOrigin="anonymous" />
      </Head>
      <div style={{ textAlign: 'center' }}>
        <h1>Trending Projects</h1>
        <div>
        {
          data ? data.map(project =>
            <p key={project}>
              <Link href='/[user]/[repo]' as={`/${project}`} onMouseEnter={handleMouseEnter}>
                {project}
              </Link>
            </p>
          ) : 'loading...'
        }
        </div>
      </div>
    </>
  )
}


================================================
FILE: examples/refetch-interval/README.md
================================================
# Refetch Interval

## One-Click Deploy

Deploy your own SWR project with Vercel Now.

[![Deploy with Vercel Now](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/refetch-interval)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/refetch-interval
cd refetch-interval
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how to make SWR fetch the API again in an interval automatically to ensure the data is up-to-date.


================================================
FILE: examples/refetch-interval/components/button.js
================================================
export default function Button({ children, ...props }) {
  return <div><button {...props}>
    {children}
    <style jsx>{`
      button {
        width: 100px;
        height: 40px;
        border: none;
        color: #fff;
        background: #03a9f4;
        font-size: 1rem;
        border-radius: 5px;
      }
    `}</style>
  </button></div>
}

================================================
FILE: examples/refetch-interval/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/refetch-interval/package.json
================================================
{
  "name": "refetch-interval",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/refetch-interval/pages/api/data.js
================================================
// an simple endpoint for getting current list
let list = []

export default function api(req, res) {
  if (req.query.add) {
    list.push(req.query.add)
  } else if (req.query.clear) {
    list = []
  }
  res.json(list)
}


================================================
FILE: examples/refetch-interval/pages/index.js
================================================
import { useState } from 'react'
import Button from '../components/button'
import fetch from '../libs/fetch'

import useSWR from 'swr'

export default function Index() {
  const { data, mutate } = useSWR('/api/data', fetch, {
    // revalidate the data per second
    refreshInterval: 1000
  })
  const [value, setValue] = useState('')

  if (!data) return <h1>loading...</h1>

  return (
    <div>
      <h1>Refetch Interval (1s)</h1>
      <h2>Todo List</h2>
      <form onSubmit={async ev => {
        ev.preventDefault()
        setValue('')
        await fetch(`/api/data?add=${value}`)
        mutate()
      }}>
        <input placeholder='enter something' value={value} onChange={ev => setValue(ev.target.value)} />
      </form>
      <ul>
        {data.map(item => <li key={item}>{item}</li>)}
      </ul>
      <Button onClick={async () => {
        await fetch(`/api/data?clear=1`)
        mutate()
      }}>Clear All</Button>
    </div>
  )
}


================================================
FILE: examples/server-render/README.md
================================================
# Server Render

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/server-render)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/server-render
cd server-render
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

This example shows how to combine Next.js getServerSideProps with the SWR `fallbackData` option to support Server-Side Rendering.

The application will fetch the data server-side and then receive it as props, that data will be passed as `fallbackData` to SWR, once the application starts client-side SWR will revalidate it against the API and update the DOM, if it's required, with the new data.


================================================
FILE: examples/server-render/libs/fetcher.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/server-render/package.json
================================================
{
  "name": "server-render",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/server-render/pages/[pokemon].js
================================================
import Link from 'next/link'
import fetcher from '../libs/fetcher'

import useSWR from 'swr'

const getURL = pokemon => `https://pokeapi.co/api/v2/pokemon/${pokemon}`

export default function Pokemon({ pokemon, fallbackData }) {
  const { data } = useSWR(getURL(pokemon), fetcher, { fallbackData })

  return (
    <div>
      <h1>{pokemon}</h1>
      {data ? (
        <div>
          <figure>
            <img src={data.sprites.front_default} />
          </figure>
          <p>height: {data.height}</p>
          <p>weight: {data.weight}</p>
          <ul>
            {data.types.map(({ type }) => (
              <li key={type.name}>{type.name}</li>
            ))}
          </ul>
        </div>
      ) : (
        'loading...'
      )}
      <br />
      <br />
      <Link href="/">
        Back
      </Link>
    </div>
  )
}

export async function getServerSideProps({ query }) {
  const data = await fetcher(getURL(query.pokemon))
  return { props: { fallbackData: data, pokemon: query.pokemon } }
}

================================================
FILE: examples/server-render/pages/index.js
================================================
import Link from 'next/link'
import fetcher from '../libs/fetcher'

import useSWR from 'swr'

const URL = 'https://pokeapi.co/api/v2/pokemon/'

export default function Home({ fallbackData }) {
  const { data } = useSWR(URL, fetcher, { fallbackData })

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <div>
        {data && data.results
          ? data.results.map(pokemon => (
              <p key={pokemon.name}>
                <Link href="/[pokemon]" as={`/${pokemon.name}`}>
                  {pokemon.name}
                </Link>
              </p>
            ))
          : 'loading...'}
      </div>
    </div>
  )
}

export async function getServerSideProps() {
  const data = await fetcher(URL)
  return { props: { fallbackData: data } }
}

================================================
FILE: examples/storage-tab-sync/README.md
================================================
# Storage Tab Sync

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/storage-tab-sync)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/storage-tab-sync
cd storage-tab-sync
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how you could use SWR to synchronize localStorage values between tabs.


================================================
FILE: examples/storage-tab-sync/libs/storage.js
================================================
export default async function storage(key) {
  const value = localStorage.getItem(key)
  if (!value) return undefined
  return JSON.parse(value)
}


================================================
FILE: examples/storage-tab-sync/package.json
================================================
{
  "name": "storage-tab-sync",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/storage-tab-sync/pages/index.js
================================================
import storage from '../libs/storage'

import useSWR, { mutate } from 'swr'

export default function Index() {
  const { data = { name: "" } } = useSWR('user-name', storage)

  function handleChange(event) {
    localStorage.setItem(
      'user-name',
      JSON.stringify({ name: event.target.value })
    )
    mutate('user-name', { name: event.target.value })
  }

  return <div style={{ textAlign: 'center' }}>
    <label htmlFor="name">Name</label>
    <input id="name" name="name" type="text" value={data.name} onChange={handleChange} />
  </div>
}


================================================
FILE: examples/subscription/README.md
================================================
# Subscription

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/subscription)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/subscription
cd subscription
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how you could use SWR to subscribe async observable data into your app.


================================================
FILE: examples/subscription/package.json
================================================
{
  "name": "subscription",
  "private": true,
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/subscription/pages/index.js
================================================
import React from "react"
import useSWRSubscription from "swr/subscription"
import EventEmitter from "events"

const event = new EventEmitter()

// Simulating an external data source.
let stopped = false
async function start () {
  for (let i = 0; i < 100; i++) {
    await new Promise(res => setTimeout(res, 1000))
    if (stopped) return
    if (i % 3 === 0 && i !== 0) {
      event.emit("error", new Error("error: " + i));
    } else {
      event.emit("data", "state: " + i);
    }
  }
}

export default function page() {
  const { data, error } = useSWRSubscription('my-sub', (key, { next }) => {
    event.on("data", (value) => next(undefined, value));
    event.on("error", (err) => next(err));
    start();
    return () => {
      stopped = true;
    };
  })

  return (
    <div>
      <h3>SWR Subscription</h3>
      <h4>Received every second, error when data is times of 3</h4>
      <div>{data}</div>
      <div>{error ? error.message : ""}</div>
    </div>
  )
}


================================================
FILE: examples/suspense/README.md
================================================
# Basic

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/suspense)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/suspense
cd suspense
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how to use the SWR suspense option with React suspense.


================================================
FILE: examples/suspense/app/layout.jsx
================================================
export default function RootLayout({
  children
}) {
  return (
    <html lang="en">
      <body style={{ textAlign: 'center' }}>{children}</body>
    </html>
  )
}


================================================
FILE: examples/suspense/app/rsc/[user]/[repo]/error.jsx
================================================
'use client'
export default function ErrorPage() {
  return <div>Error happen</div>;
}

================================================
FILE: examples/suspense/app/rsc/[user]/[repo]/loading.jsx
================================================
export default function Loading() {
  return <div>Loading...</div>;
}

================================================
FILE: examples/suspense/app/rsc/[user]/[repo]/page.jsx
================================================
import Repo from './repo'
import fetcher from '../../../../libs/fetch'
import Link from 'next/link'
import { Suspense } from 'react'
const Page = ({ params }) => {
  const { user, repo } = params
  const id = `${user}/${repo}`
  const serverData = fetcher('http://localhost:3000/api/data?id=' + id)
  return (
    <div>
      <div>Repo: {id}</div>
      <Suspense fallback={<div>Loading stats</div>}>
        <Repo serverData={serverData} id={id} />
      </Suspense>
      <br />
      <br />
      <Link href="/rsc">Back</Link>
    </div>
  )
}


export default Page

================================================
FILE: examples/suspense/app/rsc/[user]/[repo]/repo.jsx
================================================
'use client'
import fetcher from '../../../../libs/fetch'
import useSWR from 'swr'

const Repo = ({ id, serverData }) => {
  const { data } = useSWR('/api/data?id=' + id, fetcher, { suspense: true, fallbackData: serverData })
  return (
    <>
      {data ? (
        <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div>
      ) : null}

    </>
  )
}

export default Repo

================================================
FILE: examples/suspense/app/rsc/loading.jsx
================================================
export default function Loading() {
  return <div>Loading...</div>;
}

================================================
FILE: examples/suspense/app/rsc/page.jsx
================================================
import fetcher from '../../libs/fetch'
import Repos from './repos'
const Page = () => {
  const serverData = fetcher('http://localhost:3000/api/data')
  return <Repos serverData={serverData} />
}

export default Page


================================================
FILE: examples/suspense/app/rsc/repos.jsx
================================================
'use client'
import useSWR from 'swr'
import fetcher from '../../libs/fetch'
import Link from 'next/link'

const Repos = ({ serverData }) => {
  const { data } = useSWR('/api/data', fetcher, {
    suspense: true,
    fallbackData: serverData
  })
  return (
    <>
      {data.map(project => (
        <p key={project}>
          <Link href={`/rsc/${project}`}>
            {project}
          </Link>
        </p>
      ))}
    </>
  )
}

export default Repos

================================================
FILE: examples/suspense/components/error-handling.js
================================================
import React from 'react'

export default class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null }
  static getDerivedStateFromError(error) {
    return {
      hasError: true,
      error
    }
  }
  render() {
    if (this.state.hasError) {
      return this.props.fallback
    }
    return this.props.children
  }
}


================================================
FILE: examples/suspense/libs/fetch.js
================================================
export default async function fetcher(...args) {
  const res = await fetch(...args)
  return res.json()
}


================================================
FILE: examples/suspense/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.


================================================
FILE: examples/suspense/package.json
================================================
{
  "name": "suspense",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/suspense/pages/[user]/[repo].js
================================================
import { Suspense } from 'react'
import Link from 'next/link'
import fetcher from '../../libs/fetch'
import ErrorHandling from '../../components/error-handling'
import useSWR from 'swr'

const Detail = ({ id, serverData }) => {
  const { data } = useSWR('/api/data?id=' + id, fetcher, { suspense: true, fallbackData: serverData })

  return (
    <>
      {data ? (
        <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div>
      ) : null}
    </>
  )
}

export default function Repo({ id, serverData }) {
  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      <Suspense fallback={<div>loading...</div>}>
        <ErrorHandling fallback={<div>oooops!</div>}>
          <Detail id={id} serverData={serverData}></Detail>
        </ErrorHandling>
      </Suspense>
      <br />
      <br />
      <Link href="/">Back</Link>
    </div>
  )
}

export const getServerSideProps = async ({ params }) => {
  const { user, repo } = params
  const id = `${user}/${repo}`
  const data = await fetcher('http://localhost:3000/api/data?id=' + id).catch(() => {})
  return { props: { serverData: data, id } }
}

================================================
FILE: examples/suspense/pages/api/data.js
================================================
const projects = [
  'facebook/flipper',
  'vuejs/vuepress',
  'rust-lang/rust',
  'vercel/next.js',
  'emperor/clothes'
]

export default function api(req, res) {
  if (req.query.id) {
    if (req.query.id === projects[4]) {
      setTimeout(() => {
        res.json({ msg: 'not found' })
      })
    } else {
      // a slow endpoint for getting repo data
      fetch(`https://api.github.com/repos/${req.query.id}`)
        .then(res => res.json())
        .then(data => {
          setTimeout(() => {
            res.json(data)
          }, 2000)
        })
    }
  } else {
    setTimeout(() => {
      res.json(projects)
    }, 2000)
  }
}


================================================
FILE: examples/suspense/pages/index.js
================================================
import { Suspense } from 'react'
import Link from 'next/link'
import fetcher from '../libs/fetch'

import useSWR from 'swr'

const Repos = ({ serverData }) => {
  const { data } = useSWR('/api/data', fetcher, {
    suspense: true,
    fallbackData: serverData
  })

  return (
    <>
      {data.map(project => (
        <p key={project}>
          <Link href="/[user]/[repo]" as={`/${project}`}>
            {project}
          </Link>
        </p>
      ))}
    </>
  )
}

export default function Index({ serverData }) {
  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <Suspense fallback={<div>loading...</div>}>
        <Repos serverData={serverData}></Repos>
      </Suspense>
    </div>
  )
}

export const getServerSideProps = async () => {
  const data = await fetcher('http://localhost:3000/api/data')
  return { props: { serverData: data } }
}


================================================
FILE: examples/suspense-global/README.md
================================================
# Basic

## One-Click Deploy

Deploy your own SWR project with Vercel.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/suspense)

## How to Use

Download the example:

```bash
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/suspense
cd suspense
```

Install it and run:

```bash
yarn
yarn dev
# or
npm install
npm run dev
```

## The Idea behind the Example

Show how to use the SWR suspense option with React suspense.


================================================
FILE: examples/suspense-global/components/error-handling.ts
================================================
import React from 'react'

export default class ErrorBoundary extends React.Component<any> {
  state = { hasError: false, error: null }
  static getDerivedStateFromError(error: any) {
    return {
      hasError: true,
      error
    }
  }
  render() {
    if (this.state.hasError) {
      return this.props.fallback
    }
    return this.props.children
  }
}


================================================
FILE: examples/suspense-global/global-swr-config.tsx
================================================
'use client'

import { SWRConfig } from 'swr'

import fetcher from './libs/fetch'

declare module 'swr' {
  interface SWRGlobalConfig {
    suspense: true
  }
}

export function GlobalSWRConfig({ children }: { children: React.ReactNode }) {
  return (
    <SWRConfig
      value={{
        fetcher,
        suspense: true
      }}
    >
      {children}
    </SWRConfig>
  )
}


================================================
FILE: examples/suspense-global/libs/fetch.ts
================================================
export default async function fetcher(...args: [any]) {
  const res = await fetch(...args)
  if (!res.ok) {
    throw new Error('An error occurred while fetching the data.')
  } else {
    return res.json()
  }
}


================================================
FILE: examples/suspense-global/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.


================================================
FILE: examples/suspense-global/package.json
================================================
{
  "name": "suspense-global",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest",
    "swr": "latest"
  },
  "scripts": {
    "dev": "next",
    "start": "next start",
    "build": "next build"
  }
}


================================================
FILE: examples/suspense-global/pages/[user]/[repo].tsx
================================================
import dynamic from 'next/dynamic'
import Link from 'next/link'
import { Suspense } from 'react'
import ErrorHandling from '../../components/error-handling'
import { useRouter } from 'next/router'

const Detail = dynamic(() => import('./detail'), {
  ssr: false
})

export default function Repo() {
  const router = useRouter()
  if (!router.isReady) return null
  const { user, repo } = router.query
  const id = `${user}/${repo}`

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{id}</h1>
      <Suspense fallback={<div>loading...</div>}>
        <ErrorHandling fallback={<div>oooops!</div>}>
          <Detail id={id}></Detail>
        </ErrorHandling>
      </Suspense>
      <br />
      <br />
      <Link href="/">Back</Link>
    </div>
  )
}


================================================
FILE: examples/suspense-global/pages/[user]/detail.tsx
================================================
import useSWR from 'swr'
import { RepoData } from '../api/data'

const Detail = ({ id }: { id: string }) => {
  const { data } = useSWR<RepoData>('/api/data?id=' + id)

  return (
    <>
      {data ? (
        <div>
          <p>forks: {data.forks_count}</p>
          <p>stars: {data.stargazers_count}</p>
          <p>watchers: {data.watchers}</p>
        </div>
      ) : null}
    </>
  )
}

export default Detail


================================================
FILE: examples/suspense-global/pages/_app.tsx
================================================
import type { AppProps } from 'next/app'
import { GlobalSWRConfig } from 'global-swr-config'

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <GlobalSWRConfig>
      <Component {...pageProps} />
    </GlobalSWRConfig>
  )
}


================================================
FILE: examples/suspense-global/pages/api/data.ts
================================================
import { NextApiRequest, NextApiResponse } from 'next'

const projects = [
  'facebook/flipper',
  'vuejs/vuepress',
  'rust-lang/rust',
  'vercel/next.js',
  'emperor/clothes'
] as const

export type ProjectsData = typeof projects

export interface RepoData {
  forks_count: number
  stargazers_count: number
  watchers: number
}

export default function api(req: NextApiRequest, res: NextApiResponse) {
  if (req.query.id) {
    if (req.query.id === projects[4]) {
      setTimeout(() => {
        res.status(404).json({ msg: 'not found' })
      })
    } else {
      // a slow endpoint for getting repo data
      fetch(`https://api.github.com/repos/${req.query.id}`)
        .then(res => res.json())
        .then(data => {
          setTimeout(() => {
            res.json(data)
          }, 2000)
        })
    }
  } else {
    setTimeout(() => {
      res.json(projects)
    }, 2000)
  }
}


================================================
FILE: examples/suspense-global/pages/index.tsx
================================================
import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const Repos = dynamic(() => import('./repos'), {
  ssr: false
})

export default function Index() {
  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Trending Projects</h1>
      <Suspense fallback={<div>loading...</div>}>
        <Repos />
      </Suspense>
    </div>
  )
}


================================================
FILE: examples/suspense-global/pages/repos.tsx
================================================
import Link from 'next/link'
import { ProjectsData } from './api/data'

import useSWR from 'swr'

const Repos = () => {
  const { data } = useSWR<ProjectsData>('/api/data')

  return (
    <>
      {data.map(project => (
        <p key={project}>
          <Link href="/[user]/[repo]" as={`/${project}`}>
            {project}
          </Link>
        </p>
      ))}
    </>
  )
}

export default Repos


================================================
FILE: examples/suspense-global/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "noImplicitAny": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}


================================================
FILE: examples/suspense-retry/app/api/route.ts
================================================
import { NextResponse } from 'next/server'

export const GET = () => {
  return Math.random() < 0.5
    ? NextResponse.json({
        data: 'success'
      })
    : new Response('Bad', {
        status: 500
      })
}


================================================
FILE: examples/suspense-retry/app/layout.tsx
================================================
export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}


================================================
FILE: examples/suspense-retry/app/manual-retry.tsx
================================================
'use client'
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { useRemoteData, preloadRemote } from './use-remote-data'

const Demo = () => {
  const { data } = useRemoteData()
  return <div>{data}</div>
}
preloadRemote()

function Fallback({ resetErrorBoundary }: any) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <button
        onClick={() => {
          resetErrorBoundary()
        }}
      >
        retry
      </button>
    </div>
  )
}

function RemoteData() {
  return (
    <div className="App">
      <ErrorBoundary
        FallbackComponent={Fallback}
        onReset={() => {
          preloadRemote()
        }}
      >
        <Suspense fallback={<div>loading</div>}>
          <Demo />
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

export default RemoteData


================================================
FILE: examples/suspense-retry/app/page.tsx
================================================
import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const RemoteData = dynamic(() => import('./manual-retry'), {
  ssr: false
})

export default function HomePage() {
  return (
    <Suspense fallback={<div>loading component</div>}>
      <RemoteData></RemoteData>
    </Suspense>
  )
}


================================================
FILE: examples/suspense-retry/app/use-remote-data.ts
================================================
'use client'
import useSWR from 'swr'
import { preload } from 'swr'

let count = 0
const fetcher = () => {
  count++
  if (count === 1) return Promise.reject('wrong')
  return fetch('/api')
    .then(r => r.json())
    .then(r => r.data)
}

const key = 'manual-retry'

export const useRemoteData = () =>
  useSWR(key, fetcher, {
    suspense: true
  })

export const preloadRemote = () => preload(key, fetcher)


================================================
FILE: examples/suspense-retry/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.


================================================
FILE: examples/suspense-retry/next.config.js
================================================
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true,
  },
}

module.exports = nextConfig


================================================
FILE: examples/suspense-retry/package.json
================================================
{
  "name": "site",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@types/node": "^20.2.5",
    "@types/react": "^18.2.8",
    "@types/react-dom": "18.2.4",
    "next": "^latest",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "5.1.3",
    "swr": "*"
  }
}


================================================
FILE: examples/suspense-retry/pages/retry.tsx
================================================
import { Suspense } from 'react'
import dynamic from 'next/dynamic'

const RemoteData = dynamic(() => import('../app/manual-retry'), {
  ssr: false
})

export default function HomePage() {
  return (
    <Suspense fallback={<div>loading component</div>}>
      <RemoteData></RemoteData>
    </Suspense>
  )
}


================================================
FILE: examples/suspense-retry/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}


================================================
FILE: immutable/package.json
================================================
{
  "main": "../dist/immutable/index.js",
  "module": "../dist/immutable/index.mjs",
  "types": "../dist/immutable/index.d.ts",
  "private": true
}


================================================
FILE: infinite/package.json
================================================
{
  "main": "../dist/infinite/index.js",
  "module": "../dist/infinite/index.mjs",
  "types": "../dist/infinite/index.d.ts",
  "private": true
}


================================================
FILE: jest.config.build.js
================================================
import config from './jest.config.js'

const useBuildConfig = {
  ...config,
  // override to
Download .txt
gitextract__gxvvg_b/

├── .codesandbox/
│   └── ci.json
├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── config.yml
│   ├── SECURITY.md
│   └── workflows/
│       ├── install/
│       │   └── action.yml
│       ├── test-canary.yml
│       ├── test-legacy-react.yml
│       ├── test-release.yml
│       └── trigger-release.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .npmrc
├── .swcrc
├── LICENSE
├── README.md
├── _internal/
│   └── package.json
├── e2e/
│   ├── site/
│   │   ├── README.md
│   │   ├── app/
│   │   │   ├── basic-ssr/
│   │   │   │   ├── block.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── concurrent-transition/
│   │   │   │   ├── page.tsx
│   │   │   │   └── transition-demo.tsx
│   │   │   ├── issue-2702/
│   │   │   │   ├── page.tsx
│   │   │   │   └── reproduction.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── mutate-server-action/
│   │   │   │   ├── action.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── page.tsx
│   │   │   ├── partially-hydrate/
│   │   │   │   ├── layout.tsx
│   │   │   │   ├── loading.tsx
│   │   │   │   ├── page.tsx
│   │   │   │   └── use-data.tsx
│   │   │   ├── perf/
│   │   │   │   └── page.tsx
│   │   │   ├── react-server-entry/
│   │   │   │   └── page.tsx
│   │   │   ├── render-count/
│   │   │   │   └── page.tsx
│   │   │   ├── render-preload-avoid-waterfall/
│   │   │   │   └── page.tsx
│   │   │   ├── render-preload-basic/
│   │   │   │   └── page.tsx
│   │   │   ├── render-promise-suspense-error/
│   │   │   │   └── page.tsx
│   │   │   ├── render-promise-suspense-resolve/
│   │   │   │   └── page.tsx
│   │   │   ├── render-promise-suspense-shared/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-avoid-rerender/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-cached-error/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-error/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-fallback/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-fetcher/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-infinite-preload/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-initial-data/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-keep-previous/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-key-change/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-multiple-fallbacks/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-no-revalidate/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-non-promise/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-null-key/
│   │   │   │   └── page.tsx
│   │   │   ├── render-suspense-same-data/
│   │   │   │   └── page.tsx
│   │   │   ├── server-prefetch-warning/
│   │   │   │   └── page.tsx
│   │   │   ├── suspense-after-preload/
│   │   │   │   ├── page.tsx
│   │   │   │   └── remote-data.tsx
│   │   │   ├── suspense-fallback/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── promise/
│   │   │   │       └── page.tsx
│   │   │   ├── suspense-infinite-get-key/
│   │   │   │   └── page.tsx
│   │   │   ├── suspense-retry/
│   │   │   │   ├── manual-retry.tsx
│   │   │   │   ├── page.tsx
│   │   │   │   └── use-remote-data.ts
│   │   │   └── suspense-undefined-key/
│   │   │       └── page.tsx
│   │   ├── component/
│   │   │   ├── manual-retry-mutate.tsx
│   │   │   ├── manual-retry.tsx
│   │   │   ├── only-render-in-client.tsx
│   │   │   └── use-remote-data.ts
│   │   ├── lib/
│   │   │   ├── sleep.ts
│   │   │   └── use-debug-history.ts
│   │   ├── next-env.d.ts
│   │   ├── next.config.js
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── api/
│   │   │   │   ├── data.ts
│   │   │   │   └── retry.ts
│   │   │   ├── suspense-retry-19.tsx
│   │   │   └── suspense-retry-mutate.tsx
│   │   └── tsconfig.json
│   └── test/
│       ├── concurrent-transition.test.ts
│       ├── initial-render.test.ts
│       ├── issue-2702-too-many-hooks.ts
│       ├── mutate-server-action.test.ts
│       ├── perf.test.ts
│       ├── preload-scenarios.test.ts
│       ├── promise-scenarios.test.ts
│       ├── server-prefetch-warning.test.ts
│       ├── stream-ssr.test.ts
│       ├── suspense-fallback.test.ts
│       ├── suspense-scenarios.test.ts
│       ├── suspense-undefined-key.test.ts
│       └── tsconfig.json
├── eslint.config.mjs
├── examples/
│   ├── .eslintrc
│   ├── api-hooks/
│   │   ├── README.md
│   │   ├── hooks/
│   │   │   ├── use-projects.js
│   │   │   └── use-repository.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── autocomplete-suggestions/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetcher.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── suggestions.js
│   │       └── index.js
│   ├── axios/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── useRequest.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── axios-typescript/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── useRequest.ts
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── [user]/
│   │   │   │   └── [repo].tsx
│   │   │   ├── api/
│   │   │   │   └── data.ts
│   │   │   └── index.tsx
│   │   └── tsconfig.json
│   ├── basic/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── basic-typescript/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.ts
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── [user]/
│   │   │   │   └── [repo].tsx
│   │   │   ├── api/
│   │   │   │   └── data.ts
│   │   │   └── index.tsx
│   │   └── tsconfig.json
│   ├── focus-revalidate/
│   │   ├── README.md
│   │   ├── components/
│   │   │   └── button.js
│   │   ├── libs/
│   │   │   ├── auth.js
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── user.js
│   │       └── index.js
│   ├── global-fetcher/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── _app.js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── infinite/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── infinite-scroll/
│   │   ├── README.md
│   │   ├── hooks/
│   │   │   └── useOnScreen.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── local-state-sharing/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── store.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── optimistic-ui/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── _app.js
│   │   │   ├── api/
│   │   │   │   └── todos.js
│   │   │   └── index.js
│   │   └── styles.css
│   ├── optimistic-ui-immer/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── prefetch-preload/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── refetch-interval/
│   │   ├── README.md
│   │   ├── components/
│   │   │   └── button.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── server-render/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── fetcher.js
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [pokemon].js
│   │       └── index.js
│   ├── storage-tab-sync/
│   │   ├── README.md
│   │   ├── libs/
│   │   │   └── storage.js
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── subscription/
│   │   ├── README.md
│   │   ├── package.json
│   │   └── pages/
│   │       └── index.js
│   ├── suspense/
│   │   ├── README.md
│   │   ├── app/
│   │   │   ├── layout.jsx
│   │   │   └── rsc/
│   │   │       ├── [user]/
│   │   │       │   └── [repo]/
│   │   │       │       ├── error.jsx
│   │   │       │       ├── loading.jsx
│   │   │       │       ├── page.jsx
│   │   │       │       └── repo.jsx
│   │   │       ├── loading.jsx
│   │   │       ├── page.jsx
│   │   │       └── repos.jsx
│   │   ├── components/
│   │   │   └── error-handling.js
│   │   ├── libs/
│   │   │   └── fetch.js
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   └── pages/
│   │       ├── [user]/
│   │       │   └── [repo].js
│   │       ├── api/
│   │       │   └── data.js
│   │       └── index.js
│   ├── suspense-global/
│   │   ├── README.md
│   │   ├── components/
│   │   │   └── error-handling.ts
│   │   ├── global-swr-config.tsx
│   │   ├── libs/
│   │   │   └── fetch.ts
│   │   ├── next-env.d.ts
│   │   ├── package.json
│   │   ├── pages/
│   │   │   ├── [user]/
│   │   │   │   ├── [repo].tsx
│   │   │   │   └── detail.tsx
│   │   │   ├── _app.tsx
│   │   │   ├── api/
│   │   │   │   └── data.ts
│   │   │   ├── index.tsx
│   │   │   └── repos.tsx
│   │   └── tsconfig.json
│   └── suspense-retry/
│       ├── app/
│       │   ├── api/
│       │   │   └── route.ts
│       │   ├── layout.tsx
│       │   ├── manual-retry.tsx
│       │   ├── page.tsx
│       │   └── use-remote-data.ts
│       ├── next-env.d.ts
│       ├── next.config.js
│       ├── package.json
│       ├── pages/
│       │   └── retry.tsx
│       └── tsconfig.json
├── immutable/
│   └── package.json
├── infinite/
│   └── package.json
├── jest.config.build.js
├── jest.config.js
├── mutation/
│   └── package.json
├── package.json
├── playwright.config.js
├── pnpm-workspace.yaml
├── scripts/
│   └── bump-next-version.js
├── src/
│   ├── _internal/
│   │   ├── constants.ts
│   │   ├── events.ts
│   │   ├── index.react-server.ts
│   │   ├── index.ts
│   │   ├── types.ts
│   │   └── utils/
│   │       ├── cache.ts
│   │       ├── config-context.ts
│   │       ├── config.ts
│   │       ├── devtools.ts
│   │       ├── env.ts
│   │       ├── global-state.ts
│   │       ├── hash.ts
│   │       ├── helper.ts
│   │       ├── merge-config.ts
│   │       ├── middleware-preset.ts
│   │       ├── mutate.ts
│   │       ├── normalize-args.ts
│   │       ├── preload.ts
│   │       ├── resolve-args.ts
│   │       ├── serialize.ts
│   │       ├── shared.ts
│   │       ├── subscribe-key.ts
│   │       ├── timestamp.ts
│   │       ├── use-swr-config.ts
│   │       ├── web-preset.ts
│   │       └── with-middleware.ts
│   ├── immutable/
│   │   └── index.ts
│   ├── index/
│   │   ├── config.ts
│   │   ├── index.react-server.ts
│   │   ├── index.ts
│   │   ├── serialize.ts
│   │   └── use-swr.ts
│   ├── infinite/
│   │   ├── index.react-server.ts
│   │   ├── index.ts
│   │   ├── serialize.ts
│   │   └── types.ts
│   ├── mutation/
│   │   ├── index.ts
│   │   ├── state.ts
│   │   └── types.ts
│   └── subscription/
│       ├── index.ts
│       └── types.ts
├── subscription/
│   └── package.json
├── test/
│   ├── jest-setup.ts
│   ├── tsconfig.json
│   ├── type/
│   │   ├── .eslintrc
│   │   ├── config.tsx
│   │   ├── fetcher.ts
│   │   ├── helper-types.tsx
│   │   ├── internal.tsx
│   │   ├── mutate.ts
│   │   ├── mutation.ts
│   │   ├── option-fetcher.ts
│   │   ├── preload.ts
│   │   ├── subscription.ts
│   │   ├── suspense/
│   │   │   ├── helper-types.tsx
│   │   │   ├── suspense.ts
│   │   │   └── tsconfig.json
│   │   ├── trigger.ts
│   │   ├── tsconfig.json
│   │   └── utils.ts
│   ├── unit/
│   │   ├── serialize.test.ts
│   │   ├── utils.test.tsx
│   │   └── web-preset.test.ts
│   ├── use-swr-cache.test.tsx
│   ├── use-swr-concurrent-rendering.test.tsx
│   ├── use-swr-config-callbacks.test.tsx
│   ├── use-swr-config.test.tsx
│   ├── use-swr-context-config.test.tsx
│   ├── use-swr-devtools.test.tsx
│   ├── use-swr-error.test.tsx
│   ├── use-swr-fetcher.test.tsx
│   ├── use-swr-focus.test.tsx
│   ├── use-swr-immutable.test.tsx
│   ├── use-swr-infinite-preload.test.tsx
│   ├── use-swr-infinite.test.tsx
│   ├── use-swr-integration.test.tsx
│   ├── use-swr-key.test.tsx
│   ├── use-swr-laggy.test.tsx
│   ├── use-swr-legacy-react.test.tsx
│   ├── use-swr-loading.test.tsx
│   ├── use-swr-local-mutation.test.tsx
│   ├── use-swr-middlewares.test.tsx
│   ├── use-swr-node-env.test.tsx
│   ├── use-swr-offline.test.tsx
│   ├── use-swr-preload.test.tsx
│   ├── use-swr-promise.test.tsx
│   ├── use-swr-reconnect.test.tsx
│   ├── use-swr-refresh.test.tsx
│   ├── use-swr-remote-mutation.test.tsx
│   ├── use-swr-revalidate.test.tsx
│   ├── use-swr-server.test.tsx
│   ├── use-swr-streaming-ssr.test.tsx
│   ├── use-swr-subscription.test.tsx
│   ├── use-swr-suspense.test.tsx
│   └── utils.tsx
└── tsconfig.json
Download .txt
SYMBOL INDEX (762 symbols across 197 files)

FILE: e2e/site/app/basic-ssr/block.tsx
  function Block (line 6) | function Block() {

FILE: e2e/site/app/basic-ssr/page.tsx
  function BasicSSRPage (line 3) | function BasicSSRPage() {

FILE: e2e/site/app/concurrent-transition/page.tsx
  function ConcurrentTransitionPage (line 10) | function ConcurrentTransitionPage() {

FILE: e2e/site/app/concurrent-transition/transition-demo.tsx
  function DataComponent (line 14) | function DataComponent({ swrKey }: { swrKey: string }) {
  function TransitionDemo (line 25) | function TransitionDemo() {

FILE: e2e/site/app/issue-2702/page.tsx
  function Page (line 3) | function Page() {

FILE: e2e/site/app/layout.tsx
  function RootLayout (line 1) | function RootLayout({

FILE: e2e/site/app/mutate-server-action/action.tsx
  function action (line 3) | async function action(): Promise<{ result: number }> {
  function sleep (line 8) | function sleep(ms: number): Promise<void> {

FILE: e2e/site/app/page.tsx
  function Page (line 20) | function Page() {

FILE: e2e/site/app/partially-hydrate/layout.tsx
  function Layout (line 6) | function Layout({ children }: PropsWithChildren) {

FILE: e2e/site/app/partially-hydrate/loading.tsx
  function Loading (line 1) | function Loading() {

FILE: e2e/site/app/partially-hydrate/page.tsx
  function Page (line 14) | function Page() {

FILE: e2e/site/app/partially-hydrate/use-data.tsx
  function useData (line 3) | function useData() {

FILE: e2e/site/app/perf/page.tsx
  function PerformancePage (line 47) | function PerformancePage() {

FILE: e2e/site/app/react-server-entry/page.tsx
  function Page (line 4) | function Page() {

FILE: e2e/site/app/render-count/page.tsx
  function Page (line 4) | function Page() {

FILE: e2e/site/app/render-preload-avoid-waterfall/page.tsx
  function fetcherA (line 12) | async function fetcherA() {
  function fetcherB (line 17) | async function fetcherB() {
  function Preload (line 22) | function Preload({ children }: { children?: React.ReactNode }) {
  function Content (line 34) | function Content() {
  function Page (line 53) | function Page() {

FILE: e2e/site/app/render-preload-basic/page.tsx
  function fetcher (line 11) | async function fetcher() {
  function Preload (line 17) | function Preload({ children }: { children?: React.ReactNode }) {
  function Page (line 27) | function Page() {

FILE: e2e/site/app/render-promise-suspense-error/page.tsx
  function PromiseConfig (line 13) | function PromiseConfig({ children }: { children: ReactNode }) {
  function Content (line 25) | function Content() {
  function Page (line 30) | function Page() {

FILE: e2e/site/app/render-promise-suspense-resolve/page.tsx
  function fetcher (line 14) | async function fetcher() {
  function PromiseConfig (line 19) | function PromiseConfig({ children }: { children: ReactNode }) {
  function Content (line 29) | function Content() {
  function Page (line 41) | function Page() {

FILE: e2e/site/app/render-promise-suspense-shared/page.tsx
  function PromiseConfig (line 12) | function PromiseConfig({ children }: { children: ReactNode }) {
  function Item (line 18) | function Item({ id }: { id: string }) {
  function Page (line 23) | function Page() {

FILE: e2e/site/app/render-suspense-avoid-rerender/page.tsx
  function fetchValue (line 8) | async function fetchValue() {
  function Section (line 13) | function Section() {
  function Page (line 42) | function Page() {

FILE: e2e/site/app/render-suspense-cached-error/page.tsx
  function fetchWithError (line 9) | async function fetchWithError(): Promise<string> {
  function Section (line 14) | function Section() {
  function ErrorFallback (line 32) | function ErrorFallback(_: { error: Error; resetErrorBoundary: () => void...
  function Page (line 36) | function Page() {

FILE: e2e/site/app/render-suspense-error/page.tsx
  function fetchWithError (line 9) | async function fetchWithError(): Promise<string> {
  function Section (line 14) | function Section() {
  function ErrorFallback (line 22) | function ErrorFallback(_: { error: Error; resetErrorBoundary: () => void...
  function Page (line 26) | function Page() {

FILE: e2e/site/app/render-suspense-fallback/page.tsx
  function fetchGreeting (line 8) | async function fetchGreeting() {
  function Section (line 13) | function Section() {
  function Page (line 21) | function Page() {

FILE: e2e/site/app/render-suspense-fetcher/page.tsx
  function Section (line 8) | function Section() {
  function Page (line 49) | function Page() {

FILE: e2e/site/app/render-suspense-infinite-preload/page.tsx
  function fetcher (line 16) | async function fetcher(key: string) {
  function Section (line 27) | function Section() {
  function Fallback (line 50) | function Fallback() {
  function Page (line 55) | function Page() {

FILE: e2e/site/app/render-suspense-initial-data/page.tsx
  function PageContent (line 8) | function PageContent() {
  function Page (line 17) | function Page() {

FILE: e2e/site/app/render-suspense-keep-previous/page.tsx
  function fetchValue (line 8) | async function fetchValue(key: string) {
  function Section (line 13) | function Section() {
  function Fallback (line 43) | function Fallback() {
  function Page (line 47) | function Page() {

FILE: e2e/site/app/render-suspense-key-change/page.tsx
  function fetchValue (line 8) | async function fetchValue(key: string) {
  function Section (line 13) | function Section() {
  function Page (line 35) | function Page() {

FILE: e2e/site/app/render-suspense-multiple-fallbacks/page.tsx
  function fetchValue (line 8) | async function fetchValue(key: string) {
  function Section (line 17) | function Section() {
  function Page (line 32) | function Page() {

FILE: e2e/site/app/render-suspense-no-revalidate/page.tsx
  function fetchFreshValue (line 8) | async function fetchFreshValue() {
  function Preload (line 13) | function Preload({ children }: { children?: React.ReactNode }) {
  function Section (line 22) | function Section() {
  function Page (line 31) | function Page() {

FILE: e2e/site/app/render-suspense-non-promise/page.tsx
  function Section (line 7) | function Section() {
  function Page (line 15) | function Page() {

FILE: e2e/site/app/render-suspense-null-key/page.tsx
  function fetchValue (line 8) | async function fetchValue(key: string) {
  function Result (line 13) | function Result({ query }: { query: string }) {
  function Page (line 27) | function Page() {

FILE: e2e/site/app/render-suspense-same-data/page.tsx
  function fetchValue (line 8) | async function fetchValue() {
  function Section (line 13) | function Section() {
  function Page (line 35) | function Page() {

FILE: e2e/site/app/server-prefetch-warning/page.tsx
  function Content (line 8) | function Content() {
  function ServerPrefetchWarningPage (line 28) | function ServerPrefetchWarningPage() {

FILE: e2e/site/app/suspense-after-preload/page.tsx
  function HomePage (line 10) | function HomePage() {

FILE: e2e/site/app/suspense-after-preload/remote-data.tsx
  function Comp (line 22) | function Comp() {

FILE: e2e/site/app/suspense-fallback/layout.tsx
  function createPromiseData (line 3) | function createPromiseData(data: any, timeout: number) {
  function Layout (line 11) | function Layout({ children }: { children: React.ReactNode }) {

FILE: e2e/site/app/suspense-fallback/promise/page.tsx
  function Page (line 5) | function Page() {

FILE: e2e/site/app/suspense-infinite-get-key/page.tsx
  constant DATA (line 8) | const DATA: Record<string, string[]> = {
  function fetchList (line 13) | async function fetchList(key: string) {
  function List (line 18) | function List() {
  function Page (line 42) | function Page() {

FILE: e2e/site/app/suspense-retry/manual-retry.tsx
  function Fallback (line 11) | function Fallback({ resetErrorBoundary }: any) {
  function RemoteData (line 26) | function RemoteData() {

FILE: e2e/site/app/suspense-retry/page.tsx
  function HomePage (line 10) | function HomePage() {

FILE: e2e/site/app/suspense-undefined-key/page.tsx
  function Page (line 19) | function Page() {

FILE: e2e/site/component/manual-retry-mutate.tsx
  function Fallback (line 26) | function Fallback({ resetErrorBoundary }: any) {
  function RemoteData (line 42) | function RemoteData() {

FILE: e2e/site/component/manual-retry.tsx
  function Fallback (line 10) | function Fallback({ resetErrorBoundary }: any) {
  function RemoteData (line 25) | function RemoteData() {

FILE: e2e/site/component/only-render-in-client.tsx
  function OnlyRenderInClient (line 5) | function OnlyRenderInClient({

FILE: e2e/site/lib/sleep.ts
  function sleep (line 1) | function sleep(ms: number) {

FILE: e2e/site/pages/api/data.ts
  type Data (line 4) | type Data = {
  function handler (line 8) | function handler(

FILE: e2e/site/pages/api/retry.ts
  type Data (line 3) | type Data = {
  function handler (line 7) | function handler(

FILE: e2e/site/pages/suspense-retry-19.tsx
  function HomePage (line 8) | function HomePage() {

FILE: e2e/site/pages/suspense-retry-mutate.tsx
  function HomePage (line 8) | function HomePage() {

FILE: examples/api-hooks/hooks/use-projects.js
  function useProjects (line 5) | function useProjects() {

FILE: examples/api-hooks/hooks/use-repository.js
  function useRepository (line 5) | function useRepository(id) {

FILE: examples/api-hooks/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/api-hooks/pages/[user]/[repo].js
  function Repo (line 4) | function Repo() {

FILE: examples/api-hooks/pages/index.js
  function Index (line 4) | function Index() {

FILE: examples/autocomplete-suggestions/libs/fetcher.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/autocomplete-suggestions/pages/api/suggestions.js
  function suggestions (line 228) | function suggestions(req, res) {

FILE: examples/autocomplete-suggestions/pages/index.js
  function Index (line 14) | function Index() {

FILE: examples/axios-typescript/libs/useRequest.ts
  type GetRequest (line 4) | type GetRequest = AxiosRequestConfig | null
  type Return (line 6) | interface Return<Data, Error>
  type Config (line 15) | interface Config<Data = unknown, Error = unknown>
  function useRequest (line 23) | function useRequest<Data = unknown, Error = unknown>(

FILE: examples/axios-typescript/pages/[user]/[repo].tsx
  function Repo (line 5) | function Repo() {

FILE: examples/axios-typescript/pages/api/data.ts
  function api (line 11) | function api(req: NextApiRequest, res: NextApiResponse) {

FILE: examples/axios-typescript/pages/index.tsx
  function Index (line 5) | function Index() {

FILE: examples/axios/libs/useRequest.js
  function useRequest (line 4) | function useRequest(request, { fallbackData, ...config } = {}) {

FILE: examples/axios/pages/[user]/[repo].js
  function Repo (line 5) | function Repo() {

FILE: examples/axios/pages/api/data.js
  function api (line 7) | function api(req, res) {

FILE: examples/axios/pages/index.js
  function Index (line 5) | function Index() {

FILE: examples/basic-typescript/libs/fetch.ts
  function fetcher (line 1) | async function fetcher<JSON = any>(

FILE: examples/basic-typescript/pages/[user]/[repo].tsx
  function Repo (line 6) | function Repo() {

FILE: examples/basic-typescript/pages/api/data.ts
  function api (line 10) | function api(req: NextApiRequest, res: NextApiResponse) {

FILE: examples/basic-typescript/pages/index.tsx
  function HomePage (line 6) | function HomePage() {

FILE: examples/basic/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/basic/pages/[user]/[repo].js
  function Repo (line 6) | function Repo() {

FILE: examples/basic/pages/api/data.js
  function api (line 5) | function api(req, res) {

FILE: examples/basic/pages/index.js
  function Index (line 6) | function Index() {

FILE: examples/focus-revalidate/components/button.js
  function Button (line 1) | function Button({ children, ...props }) {

FILE: examples/focus-revalidate/libs/auth.js
  function login (line 3) | function login() {
  function logout (line 7) | function logout() {

FILE: examples/focus-revalidate/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/focus-revalidate/pages/api/user.js
  function user (line 2) | function user(req, res) {

FILE: examples/focus-revalidate/pages/index.js
  function Index (line 7) | function Index() {

FILE: examples/global-fetcher/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/global-fetcher/pages/[user]/[repo].js
  function Repo (line 5) | function Repo() {

FILE: examples/global-fetcher/pages/_app.js
  class MyApp (line 6) | class MyApp extends App {
    method render (line 7) | render() {

FILE: examples/global-fetcher/pages/api/data.js
  function api (line 5) | function api(req, res) {

FILE: examples/global-fetcher/pages/index.js
  function Index (line 5) | function Index() {

FILE: examples/infinite-scroll/hooks/useOnScreen.js
  function useOnScreen (line 3) | function useOnScreen(ref) {

FILE: examples/infinite-scroll/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/infinite-scroll/pages/index.js
  constant PAGE_SIZE (line 7) | const PAGE_SIZE = 6
  function App (line 17) | function App() {

FILE: examples/infinite/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/infinite/pages/index.js
  constant PAGE_SIZE (line 6) | const PAGE_SIZE = 6
  function App (line 8) | function App() {

FILE: examples/local-state-sharing/pages/index.js
  function Profile (line 5) | function Profile() {
  function Other (line 31) | function Other() {
  function Index (line 46) | function Index() {

FILE: examples/optimistic-ui-immer/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/optimistic-ui-immer/pages/api/data.js
  function shouldFail (line 3) | function shouldFail() {
  function api (line 7) | function api(req, res) {

FILE: examples/optimistic-ui-immer/pages/index.js
  function Index (line 7) | function Index() {

FILE: examples/optimistic-ui/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/optimistic-ui/pages/_app.js
  function App (line 3) | function App({ Component, pageProps }) {

FILE: examples/optimistic-ui/pages/api/todos.js
  function getTodos (line 4) | async function getTodos() {
  function addTodo (line 9) | async function addTodo(todo) {
  function api (line 19) | async function api(req, res) {

FILE: examples/optimistic-ui/pages/index.js
  function App (line 6) | function App() {

FILE: examples/prefetch-preload/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/prefetch-preload/pages/[user]/[repo].js
  function prefetchParent (line 8) | function prefetchParent() {
  function Repo (line 16) | function Repo() {

FILE: examples/prefetch-preload/pages/api/data.js
  function api (line 5) | function api(req, res) {

FILE: examples/prefetch-preload/pages/index.js
  function prefetchData (line 8) | function prefetchData() {
  function prefetchItem (line 16) | function prefetchItem(project) {
  function prefetchWithProjects (line 23) | function prefetchWithProjects() {
  function Index (line 31) | function Index() {

FILE: examples/refetch-interval/components/button.js
  function Button (line 1) | function Button({ children, ...props }) {

FILE: examples/refetch-interval/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/refetch-interval/pages/api/data.js
  function api (line 4) | function api(req, res) {

FILE: examples/refetch-interval/pages/index.js
  function Index (line 7) | function Index() {

FILE: examples/server-render/libs/fetcher.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/server-render/pages/[pokemon].js
  function Pokemon (line 8) | function Pokemon({ pokemon, fallbackData }) {
  function getServerSideProps (line 39) | async function getServerSideProps({ query }) {

FILE: examples/server-render/pages/index.js
  constant URL (line 6) | const URL = 'https://pokeapi.co/api/v2/pokemon/'
  function Home (line 8) | function Home({ fallbackData }) {
  function getServerSideProps (line 29) | async function getServerSideProps() {

FILE: examples/storage-tab-sync/libs/storage.js
  function storage (line 1) | async function storage(key) {

FILE: examples/storage-tab-sync/pages/index.js
  function Index (line 5) | function Index() {

FILE: examples/subscription/pages/index.js
  function start (line 9) | async function start () {
  function page (line 21) | function page() {

FILE: examples/suspense-global/components/error-handling.ts
  class ErrorBoundary (line 3) | class ErrorBoundary extends React.Component<any> {
    method getDerivedStateFromError (line 5) | static getDerivedStateFromError(error: any) {
    method render (line 11) | render() {

FILE: examples/suspense-global/global-swr-config.tsx
  type SWRGlobalConfig (line 8) | interface SWRGlobalConfig {
  function GlobalSWRConfig (line 13) | function GlobalSWRConfig({ children }: { children: React.ReactNode }) {

FILE: examples/suspense-global/libs/fetch.ts
  function fetcher (line 1) | async function fetcher(...args: [any]) {

FILE: examples/suspense-global/pages/[user]/[repo].tsx
  function Repo (line 11) | function Repo() {

FILE: examples/suspense-global/pages/_app.tsx
  function MyApp (line 4) | function MyApp({ Component, pageProps }: AppProps) {

FILE: examples/suspense-global/pages/api/data.ts
  type ProjectsData (line 11) | type ProjectsData = typeof projects
  type RepoData (line 13) | interface RepoData {
  function api (line 19) | function api(req: NextApiRequest, res: NextApiResponse) {

FILE: examples/suspense-global/pages/index.tsx
  function Index (line 8) | function Index() {

FILE: examples/suspense-retry/app/layout.tsx
  function RootLayout (line 1) | function RootLayout({

FILE: examples/suspense-retry/app/manual-retry.tsx
  function Fallback (line 12) | function Fallback({ resetErrorBoundary }: any) {
  function RemoteData (line 27) | function RemoteData() {

FILE: examples/suspense-retry/app/page.tsx
  function HomePage (line 8) | function HomePage() {

FILE: examples/suspense-retry/pages/retry.tsx
  function HomePage (line 8) | function HomePage() {

FILE: examples/suspense/app/layout.jsx
  function RootLayout (line 1) | function RootLayout({

FILE: examples/suspense/app/rsc/[user]/[repo]/error.jsx
  function ErrorPage (line 2) | function ErrorPage() {

FILE: examples/suspense/app/rsc/[user]/[repo]/loading.jsx
  function Loading (line 1) | function Loading() {

FILE: examples/suspense/app/rsc/loading.jsx
  function Loading (line 1) | function Loading() {

FILE: examples/suspense/components/error-handling.js
  class ErrorBoundary (line 3) | class ErrorBoundary extends React.Component {
    method getDerivedStateFromError (line 5) | static getDerivedStateFromError(error) {
    method render (line 11) | render() {

FILE: examples/suspense/libs/fetch.js
  function fetcher (line 1) | async function fetcher(...args) {

FILE: examples/suspense/pages/[user]/[repo].js
  function Repo (line 23) | function Repo({ id, serverData }) {

FILE: examples/suspense/pages/api/data.js
  function api (line 9) | function api(req, res) {

FILE: examples/suspense/pages/index.js
  function Index (line 26) | function Index({ serverData }) {

FILE: scripts/bump-next-version.js
  function bumpVersion (line 14) | function bumpVersion(version) {

FILE: src/_internal/constants.ts
  constant INFINITE_PREFIX (line 1) | const INFINITE_PREFIX = '$inf$'

FILE: src/_internal/events.ts
  constant FOCUS_EVENT (line 1) | const FOCUS_EVENT = 0
  constant RECONNECT_EVENT (line 2) | const RECONNECT_EVENT = 1
  constant MUTATE_EVENT (line 3) | const MUTATE_EVENT = 2
  constant ERROR_REVALIDATE_EVENT (line 4) | const ERROR_REVALIDATE_EVENT = 3

FILE: src/_internal/types.ts
  type GlobalState (line 12) | type GlobalState = [
  type FetcherResponse (line 34) | type FetcherResponse<Data = unknown> = Data | Promise<Data>
  type BareFetcher (line 48) | type BareFetcher<Data = unknown> = (
  type Fetcher (line 65) | type Fetcher<
  type BlockingData (line 95) | type BlockingData<
  type InternalConfiguration (line 117) | interface InternalConfiguration {
  type PublicConfiguration (line 138) | interface PublicConfiguration<
  type FullConfiguration (line 322) | type FullConfiguration<
  type ProviderConfiguration (line 338) | type ProviderConfiguration = {
  type SWRHook (line 393) | interface SWRHook {
  type ArgumentsTuple (line 717) | type ArgumentsTuple = readonly [any, ...unknown[]]
  type Arguments (line 748) | type Arguments =
  type Key (line 774) | type Key = Arguments | (() => Arguments)
  type StrictTupleKey (line 781) | type StrictTupleKey = ArgumentsTuple | null | undefined | false
  type StrictKey (line 788) | type StrictKey = StrictTupleKey | (() => StrictTupleKey)
  type MutatorCallback (line 814) | type MutatorCallback<Data = any> = (
  type MutatorOptions (line 829) | type MutatorOptions<Data = any, MutationData = Data> = {
  type MutatorConfig (line 886) | type MutatorConfig = {
  type Broadcaster (line 891) | type Broadcaster<Data = any, Error = any> = (
  type State (line 913) | type State<Data = any, Error = any> = {
  type MutatorFn (line 924) | type MutatorFn<Data = any> = (
  type MutatorWrapper (line 931) | type MutatorWrapper<Fn> = Fn extends (
  type Mutator (line 943) | type Mutator<Data = any> = MutatorWrapper<MutatorFn<Data>>
  type ScopedMutator (line 945) | interface ScopedMutator {
  type KeyedMutator (line 970) | type KeyedMutator<Data> = <MutationData = Data>(
  type SWRConfiguration (line 975) | type SWRConfiguration<
  type IsLoadingResponse (line 984) | type IsLoadingResponse<
  type SWRDefaultOptions (line 993) | type SWRDefaultOptions<Data> = SWRConfiguration<Data, Error, Fetcher<Dat...
  type SWRConfigurationWithOptionalFallback (line 994) | type SWRConfigurationWithOptionalFallback<Options> =
  type SWRResponse (line 1013) | interface SWRResponse<Data = any, Error = any, Config = any> {
  type KeyLoader (line 1054) | type KeyLoader<Args extends Arguments = Arguments> =
  type RevalidatorOptions (line 1058) | interface RevalidatorOptions {
  type Revalidator (line 1063) | type Revalidator = (
  type RevalidateEvent (line 1067) | type RevalidateEvent =
  type RevalidateCallbackReturnType (line 1072) | type RevalidateCallbackReturnType = {
  type RevalidateCallback (line 1078) | type RevalidateCallback = <K extends RevalidateEvent>(
  type Cache (line 1095) | interface Cache<Data = any> {
  type StateDependencies (line 1127) | interface StateDependencies {

FILE: src/_internal/utils/env.ts
  constant IS_REACT_LEGACY (line 4) | const IS_REACT_LEGACY = !React.useId
  constant IS_SERVER (line 6) | const IS_SERVER = !isWindowDefined || isLegacyDeno

FILE: src/_internal/utils/helper.ts
  constant EMPTY_CACHE (line 5) | const EMPTY_CACHE = {}
  constant INITIAL_CACHE (line 6) | const INITIAL_CACHE: Record<string, any> = {}
  constant STR_UNDEFINED (line 8) | const STR_UNDEFINED = 'undefined'

FILE: src/_internal/utils/middleware-preset.ts
  constant BUILT_IN_MIDDLEWARE (line 4) | const BUILT_IN_MIDDLEWARE = devtoolsUse.concat(preload)

FILE: src/_internal/utils/mutate.ts
  type KeyFilter (line 23) | type KeyFilter = (key?: Arguments) => boolean
  type MutateState (line 24) | type MutateState<Data> = State<Data, any> & {
  function internalMutate (line 41) | async function internalMutate<Data>(

FILE: src/_internal/utils/preload.ts
  type PreloadFetcher (line 15) | type PreloadFetcher<

FILE: src/_internal/utils/shared.ts
  constant UNDEFINED (line 9) | const UNDEFINED = (/*#__NOINLINE__*/ noop()) as undefined
  constant OBJECT (line 11) | const OBJECT = Object

FILE: src/_internal/utils/subscribe-key.ts
  type Callback (line 1) | type Callback = (...args: any[]) => any

FILE: src/index/index.ts
  type SWRGlobalConfig (line 14) | interface SWRGlobalConfig {

FILE: src/index/use-swr.ts
  constant WITH_DEDUPE (line 78) | const WITH_DEDUPE = { dedupe: true }
  type DefinitelyTruthy (line 80) | type DefinitelyTruthy<T> = false extends T
  function next (line 729) | function next() {
  function execute (line 744) | function execute() {
  method data (line 814) | get data() {
  method error (line 818) | get error() {
  method isValidating (line 822) | get isValidating() {
  method isLoading (line 826) | get isLoading() {

FILE: src/infinite/index.ts
  constant EMPTY_PROMISE (line 40) | const EMPTY_PROMISE = Promise.resolve() as Promise<undefined>
  method data (line 327) | get data() {
  method error (line 330) | get error() {
  method isValidating (line 333) | get isValidating() {
  method isLoading (line 336) | get isLoading() {

FILE: src/infinite/types.ts
  type FetcherResponse (line 12) | type FetcherResponse<Data = unknown> = Data | Promise<Data>
  type SWRInfiniteFetcher (line 14) | type SWRInfiniteFetcher<
  type SWRInfiniteKeyLoader (line 23) | type SWRInfiniteKeyLoader<
  type SWRInfiniteCompareFn (line 28) | interface SWRInfiniteCompareFn<Data = any> {
  type SWRInfiniteConfiguration (line 32) | interface SWRInfiniteConfiguration<
  type SWRInfiniteRevalidateFn (line 46) | interface SWRInfiniteRevalidateFn<Data = any> {
  type SWRInfiniteKeyedMutator (line 50) | type SWRInfiniteKeyedMutator<Data> = <MutationData = Data>(
  type SWRInfiniteMutatorOptions (line 55) | interface SWRInfiniteMutatorOptions<Data = any, MutationData = Data>
  type SWRInfiniteResponse (line 62) | interface SWRInfiniteResponse<Data = any, Error = any>
  type SWRInfiniteHook (line 71) | interface SWRInfiniteHook {

FILE: src/mutation/index.ts
  method data (line 128) | get data() {
  method error (line 132) | get error() {
  method isMutating (line 136) | get isMutating() {

FILE: src/mutation/types.ts
  type FetcherResponse (line 3) | type FetcherResponse<Data> = Data | Promise<Data>
  type FetcherOptions (line 5) | type FetcherOptions<ExtraArg = unknown> = Readonly<{
  type MutationFetcher (line 9) | type MutationFetcher<
  type SWRMutationConfiguration (line 21) | type SWRMutationConfiguration<
  type RemoveUndefined (line 51) | type RemoveUndefined<T> = T extends undefined ? never : T
  type IsUndefinedIncluded (line 52) | type IsUndefinedIncluded<T> = undefined extends T ? true : false
  type TriggerWithArgs (line 53) | interface TriggerWithArgs<

FILE: src/subscription/index.ts
  type SubscriptionStates (line 23) | type SubscriptionStates = [Map<string, number>, Map<string, () => void>]
  constant SUBSCRIPTION_PREFIX (line 26) | const SUBSCRIPTION_PREFIX = '$sub$'
  method data (line 98) | get data() {
  method error (line 101) | get error() {

FILE: src/subscription/types.ts
  type SWRSubscriptionOptions (line 3) | type SWRSubscriptionOptions<Data = any, Error = any> = {
  type SWRSubscription (line 7) | type SWRSubscription<
  type SWRSubscriptionResponse (line 19) | type SWRSubscriptionResponse<Data = any, Error = any> = {
  type SWRSubscriptionHook (line 24) | type SWRSubscriptionHook = <

FILE: test/type/config.tsx
  function useTestCache (line 7) | function useTestCache() {
  function useTestCustomSWRConfig (line 11) | function useTestCustomSWRConfig() {
  function useTestFullConfiguration (line 41) | function useTestFullConfiguration() {
  function testSWRResponseCachedDataTypes (line 50) | function testSWRResponseCachedDataTypes() {
  function useTestSuspense (line 62) | function useTestSuspense() {
  function useTestFallbackData (line 93) | function useTestFallbackData() {
  function useTestConfigAsSWRConfiguration (line 161) | function useTestConfigAsSWRConfiguration() {
  function useTestEmptyConfig (line 167) | function useTestEmptyConfig() {
  function useTestFallbackDataConfig (line 179) | function useTestFallbackDataConfig() {
  function useTestProviderConfig (line 188) | function useTestProviderConfig() {

FILE: test/type/fetcher.ts
  function useDataErrorGeneric (line 6) | function useDataErrorGeneric() {
  function useString (line 29) | function useString() {
  function useRecord (line 46) | function useRecord() {
  function useTuple (line 69) | function useTuple() {
  function useReadonlyTuple (line 96) | function useReadonlyTuple() {
  function useReturnString (line 156) | function useReturnString() {
  function useReturnRecord (line 210) | function useReturnRecord() {
  function useReturnTuple (line 280) | function useReturnTuple() {
  function useReturnReadonlyTuple (line 361) | function useReturnReadonlyTuple() {

FILE: test/type/helper-types.tsx
  function testDataCached (line 4) | function testDataCached() {

FILE: test/type/internal.tsx
  function rAFTyping (line 4) | function rAFTyping() {

FILE: test/type/mutate.ts
  type Case1 (line 14) | type Case1<Data = any> = MutatorFn<Data>
  type Case2 (line 15) | type Case2<Data = any> = (
  type Case3 (line 21) | type Case3<Data = any> = (
  type Case4 (line 26) | type Case4<Data = any> = (
  type Case5 (line 34) | type Case5<Data = any> = (
  type Case6 (line 42) | type Case6<Data = any> = (
  type TestCasesForMutator (line 51) | type TestCasesForMutator = [
  function useMutatorTypes (line 61) | function useMutatorTypes() {
  function useConfigMutate (line 73) | function useConfigMutate() {

FILE: test/type/mutation.ts
  function useConfigMutation (line 4) | function useConfigMutation() {

FILE: test/type/option-fetcher.ts
  function useDataErrorGeneric (line 6) | function useDataErrorGeneric() {
  function useString (line 12) | function useString() {
  function useRecord (line 33) | function useRecord() {
  function useTuple (line 65) | function useTuple() {
  function useReadonlyTuple (line 101) | function useReadonlyTuple() {
  function useReturnString (line 167) | function useReturnString() {
  function useReturnRecord (line 224) | function useReturnRecord() {
  function useReturnTuple (line 297) | function useReturnTuple() {
  function useReturnReadonlyTuple (line 384) | function useReturnReadonlyTuple() {

FILE: test/type/preload.ts
  function testPreload (line 5) | function testPreload() {

FILE: test/type/subscription.ts
  function useTestSubscription (line 5) | function useTestSubscription() {

FILE: test/type/suspense/helper-types.tsx
  type SWRGlobalConfig (line 5) | interface SWRGlobalConfig {
  function testDataCached (line 10) | function testDataCached() {

FILE: test/type/suspense/suspense.ts
  type SWRGlobalConfig (line 5) | interface SWRGlobalConfig {
  function useTestSuspense (line 10) | function useTestSuspense() {

FILE: test/type/trigger.ts
  type ExpectType (line 3) | type ExpectType = <T>(value: T) => void
  type Equal (line 6) | type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T ext...
  function useExtraParam (line 15) | function useExtraParam() {
  function useTrigger (line 24) | function useTrigger() {
  function useTriggerWithParameter (line 44) | function useTriggerWithParameter() {
  function useOnErrorThrowFalse (line 63) | function useOnErrorThrowFalse() {
  function useTestSWRMutation (line 85) | function useTestSWRMutation() {
  function useTestSWRMutationWithOptionalArgs (line 117) | function useTestSWRMutationWithOptionalArgs() {
  function useTestSWRMutationWithSWRMutate (line 133) | function useTestSWRMutationWithSWRMutate() {

FILE: test/type/utils.ts
  type ExpectType (line 1) | type ExpectType = <T>(value: T) => void

FILE: test/unit/web-preset.test.ts
  constant FOCUS_EVENT (line 3) | const FOCUS_EVENT = 'focus'
  constant VISIBILITYCHANGE_EVENT (line 4) | const VISIBILITYCHANGE_EVENT = 'visibilitychange'
  function createEventTarget (line 6) | function createEventTarget() {
  function runTests (line 14) | function runTests(propertyName) {

FILE: test/use-swr-cache.test.tsx
  function Page (line 25) | function Page() {
  function Page (line 48) | function Page() {
  function Page (line 66) | function Page() {
  function Foo (line 85) | function Foo() {
  function Page (line 89) | function Page() {
  function Foo (line 113) | function Foo() {
  function Page (line 117) | function Page() {
  function Page (line 146) | function Page() {
  method initFocus (line 154) | initFocus() {
  method initReconnect (line 158) | initReconnect() {
  function Page (line 181) | function Page() {
  function Page (line 201) | function Page() {
  function Page (line 223) | function Page() {
  function Page (line 243) | function Page() {
  function Page (line 277) | function Page() {
  function Foo (line 293) | function Foo() {
  function Bar (line 297) | function Bar() {
  function Page (line 301) | function Page() {
  function Page (line 324) | function Page() {
  function Page (line 348) | function Page() {
  function Page (line 365) | function Page() {
  function Page (line 388) | function Page() {
  function Page (line 404) | function Page() {
  function Page (line 423) | function Page() {
  function Page (line 440) | function Page() {
  function Comp (line 455) | function Comp() {
  function Wrapper (line 460) | function Wrapper() {
  function Page (line 480) | function Page() {
  function Comp (line 489) | function Comp() {

FILE: test/use-swr-concurrent-rendering.test.tsx
  function Page (line 16) | function Page() {
  function Counter (line 37) | function Counter() {
  function Body (line 52) | function Body() {
  function Page (line 62) | function Page() {

FILE: test/use-swr-config-callbacks.test.tsx
  function Page (line 11) | function Page(props: { text: string }) {
  function Page (line 46) | function Page(props: { text: string }) {
  function Page (line 91) | function Page(props: { text: string }) {
  function Page (line 138) | function Page(props: { text: string }) {
  function Page (line 179) | function Page() {
  function Page (line 208) | function Page() {

FILE: test/use-swr-config.test.tsx
  function Page (line 19) | function Page() {
  function Page (line 46) | function Page() {
  function Page (line 104) | function Page() {
  function Page (line 133) | function Page() {
  function Page (line 145) | function Page() {
  function Page (line 185) | function Page() {
  function Page (line 226) | function Page() {

FILE: test/use-swr-context-config.test.tsx
  function Page (line 22) | function Page() {
  function Page (line 46) | function Page() {
  function ChildComponent (line 53) | function ChildComponent() {

FILE: test/use-swr-devtools.test.tsx
  function Page (line 22) | function Page() {

FILE: test/use-swr-error.test.tsx
  function Page (line 15) | function Page() {
  function Page (line 37) | function Page() {
  function Page (line 62) | function Page() {
  function Page (line 96) | function Page() {
  function Page (line 136) | function Page() {
  function Page (line 168) | function Page() {
  function Page (line 200) | function Page() {
  function Page (line 233) | function Page() {
  function Page (line 261) | function Page() {
  function Page (line 295) | function Page() {
  function App (line 308) | function App() {
  function Page (line 332) | function Page() {
  function App (line 349) | function App() {
  function Page (line 372) | function Page() {
  function Page (line 404) | function Page() {
  function Page (line 435) | function Page() {
  function Page (line 454) | function Page() {
  function Foo (line 476) | function Foo() {
  function Page (line 482) | function Page() {

FILE: test/use-swr-fetcher.test.tsx
  function Page (line 19) | function Page() {
  function Page (line 46) | function Page() {
  function Page (line 81) | function Page() {
  function Page (line 118) | function Page({ fetcher }) {

FILE: test/use-swr-focus.test.tsx
  function Page (line 18) | function Page() {
  function Page (line 43) | function Page() {
  function Page (line 69) | function Page() {
  function Page (line 118) | function Page() {
  function Page (line 152) | function Page() {
  function Page (line 204) | function Page() {
  function Page (line 224) | function Page() {

FILE: test/use-swr-immutable.test.tsx
  function Component (line 25) | function Component() {
  function Page (line 30) | function Page() {
  function Component (line 69) | function Component() {
  function Page (line 73) | function Page() {
  function Component (line 110) | function Component() {
  function Page (line 114) | function Page() {
  function Component (line 158) | function Component() {
  function Page (line 162) | function Page() {
  function Page (line 207) | function Page() {
  function Data (line 261) | function Data() {
  function Page (line 277) | function Page() {
  function Page (line 308) | function Page() {

FILE: test/use-swr-infinite-preload.test.tsx
  function Page (line 22) | function Page() {
  function Page (line 37) | function Page() {
  function Page (line 55) | function Page() {
  function Comp (line 75) | function Comp() {
  function Page (line 80) | function Page() {
  function Page (line 108) | function Page() {
  function Page (line 144) | function Page() {
  function Page (line 183) | function Page() {
  function Page (line 224) | function Page() {
  function Page (line 267) | function Page() {
  function Page (line 289) | function Page() {

FILE: test/use-swr-infinite.test.tsx
  function Page (line 19) | function Page() {
  function Page (line 45) | function Page() {
  function Page (line 78) | function Page() {
  function Page (line 105) | function Page() {
  function mockAPIFetcher (line 141) | async function mockAPIFetcher(url) {
  function Page (line 161) | function Page() {
  function Page (line 216) | function Page() {
  function Page (line 266) | function Page() {
  function Page (line 319) | function Page() {
  function App (line 337) | function App() {
  function Page (line 365) | function Page() {
  function Page (line 416) | function Page() {
  function Comp (line 459) | function Comp() {
  function Page (line 477) | function Page() {
  function Comp (line 505) | function Comp() {
  function Page (line 546) | function Page() {
  function Page (line 564) | function Page() {
  function Page (line 592) | function Page() {
  function Page (line 663) | function Page() {
  function Page (line 686) | function Page() {
  function Page (line 711) | function Page() {
  function Page (line 742) | function Page() {
  function Comp (line 778) | function Comp() {
  function Page (line 810) | function Page() {
  function App (line 818) | function App() {
  function Page (line 851) | function Page() {
  function Comp (line 871) | function Comp() {
  function Page (line 905) | function Page() {
  function Comp (line 943) | function Comp() {
  function Page (line 961) | function Page() {
  function Page (line 992) | function Page() {
  function Page (line 1016) | function Page() {
  function Page (line 1048) | function Page() {
  function Page (line 1074) | function Page() {
  function Page (line 1101) | function Page() {
  function Page (line 1128) | function Page() {
  function Page (line 1174) | function Page() {
  function Content (line 1212) | function Content() {
  function Page (line 1225) | function Page() {
  function Page (line 1255) | function Page() {
  function Page (line 1288) | function Page() {
  function Page (line 1318) | function Page() {
  function Page (line 1353) | function Page() {
  function Page (line 1390) | function Page() {
  function Page (line 1428) | function Page() {
  function Page (line 1466) | function Page() {
  function Page (line 1512) | function Page() {
  function Page (line 1555) | function Page() {
  function Page (line 1599) | function Page() {
  function Page (line 1638) | function Page() {
  function Page (line 1707) | function Page() {
  function Page (line 1737) | function Page() {
  function Page (line 1769) | function Page() {
  function Page (line 1797) | function Page() {
  function Page (line 1826) | function Page() {
  function Page (line 1856) | function Page() {

FILE: test/use-swr-integration.test.tsx
  function Page (line 16) | function Page() {
  function Page (line 30) | function Page() {
  function Page (line 45) | function Page() {
  function Page (line 62) | function Page() {
  function Page (line 78) | function Page() {
  function Page (line 101) | function Page() {
  function Page (line 120) | function Page() {
  function Page (line 143) | function Page() {
  function Page (line 163) | function Page() {
  function Block (line 181) | function Block() {
  function Page (line 190) | function Page() {
  function Block (line 214) | function Block() {
  function Page (line 231) | function Page() {
  function useBroadcast3 (line 253) | function useBroadcast3() {
  function Initiator (line 261) | function Initiator() {
  function Consumer (line 273) | function Consumer() {
  function Page (line 277) | function Page() {
  function Page (line 304) | function Page() {
  function Page (line 338) | function Page() {
  function Page (line 356) | function Page() {
  function Page (line 374) | function Page() {
  function Page (line 414) | function Page() {
  function Foo (line 432) | function Foo() {
  function Page (line 439) | function Page() {
  function Page (line 511) | function Page() {
  function Page (line 572) | function Page() {
  function Page (line 606) | function Page() {

FILE: test/use-swr-key.test.tsx
  function Page (line 11) | function Page() {
  function Page (line 43) | function Page() {
  function Page (line 76) | function Page() {
  function Page (line 119) | function Page() {
  function Page (line 145) | function Page() {
  function Page (line 157) | function Page() {
  function Page (line 187) | function Page() {
  function Page (line 210) | function Page() {

FILE: test/use-swr-laggy.test.tsx
  function App (line 12) | function App() {
  function App (line 39) | function App() {
  function App (line 69) | function App() {
  function App (line 102) | function App() {
  function App (line 138) | function App() {
  function App (line 168) | function App() {
  function App (line 203) | function App() {
  function App (line 242) | function App() {

FILE: test/use-swr-legacy-react.test.tsx
  function withLegacyReact (line 9) | async function withLegacyReact(runner: () => Promise<void>) {
  function Page (line 27) | function Page() {
  function Page (line 54) | function Page() {

FILE: test/use-swr-loading.test.tsx
  function Page (line 17) | function Page() {
  function Page (line 40) | function Page() {
  function Page (line 63) | function Page() {
  function Page (line 84) | function Page() {
  function Page (line 110) | function Page() {
  function Page (line 153) | function Page() {
  function Foo (line 172) | function Foo() {
  function Page (line 180) | function Page() {
  function Foo (line 198) | function Foo() {
  function Page (line 206) | function Page() {
  function Foo (line 224) | function Foo() {
  function Page (line 238) | function Page() {
  function Page (line 268) | function Page() {
  function Page (line 297) | function Page() {
  function Page (line 352) | function Page() {
  function Page (line 362) | function Page() {

FILE: test/use-swr-local-mutation.test.tsx
  function Page (line 21) | function Page() {
  function Page (line 52) | function Page() {
  function Page (line 78) | function Page() {
  function Page (line 105) | function Page() {
  function Page (line 132) | function Page() {
  function Page (line 160) | function Page() {
  function Page (line 189) | function Page() {
  function Page (line 215) | function Page() {
  function Page (line 243) | function Page() {
  function Page (line 269) | function Page() {
  function App (line 288) | function App() {
  function Page (line 343) | function Page() {
  function Section (line 370) | function Section() {
  function Page (line 392) | function Page() {
  function Page (line 457) | function Page() {
  function Page (line 483) | function Page() {
  function Page (line 513) | function Page() {
  function Page (line 550) | function Page() {
  function Section (line 577) | function Section() {
  function Component (line 610) | function Component() {
  function Component (line 644) | function Component() {
  function Component (line 693) | function Component() {
  function Component (line 742) | function Component() {
  function Page (line 786) | function Page() {
  function Page (line 796) | function Page() {
  function Page (line 830) | function Page() {
  function Data (line 858) | function Data() {
  function Page (line 874) | function Page() {
  function Page (line 904) | function Page() {
  function Page (line 923) | function Page() {
  function Page (line 954) | function Page() {
  function Page (line 1002) | function Page() {
  function Page (line 1041) | function Page() {
  function Page (line 1073) | function Page() {
  function Page (line 1099) | function Page() {
  function useOptimisticDataMutate (line 1130) | function useOptimisticDataMutate(_key, data, fallback) {
  function Page (line 1141) | function Page() {
  function useOptimisticData1Mutate (line 1166) | function useOptimisticData1Mutate() {
  function useOptimisticData2Mutate (line 1177) | function useOptimisticData2Mutate() {
  function Page (line 1188) | function Page() {
  function Page (line 1249) | function Page() {
  function Page (line 1281) | function Page() {
  function Page (line 1319) | function Page() {
  function Page (line 1374) | function Page() {
  function Page (line 1425) | function Page() {
  function Page (line 1488) | function Page() {
  function Page (line 1541) | function Page() {
  function Page (line 1574) | function Page() {
  function Page (line 1616) | function Page() {
  function Page (line 1656) | function Page() {
  function Page (line 1721) | function Page() {
  function Page (line 1771) | function Page() {
  function Page (line 1805) | function Page() {
  function Page (line 1849) | function Page() {
  function Page (line 1879) | function Page() {
  function Page (line 1915) | function Page() {

FILE: test/use-swr-middlewares.test.tsx
  function Page (line 23) | function Page() {
  function Page (line 45) | function Page() {
  function Page (line 67) | function Page() {
  function Page (line 86) | function Page() {
  function Page (line 108) | function Page() {
  function Page (line 149) | function Page() {
  function Page (line 187) | function Page() {
  function Page (line 218) | function Page() {
  function Page (line 242) | function Page() {

FILE: test/use-swr-node-env.test.tsx
  function Page (line 23) | function Page() {
  function Page (line 36) | function Page() {

FILE: test/use-swr-offline.test.tsx
  function Page (line 22) | function Page() {
  function Page (line 50) | function Page() {

FILE: test/use-swr-preload.test.tsx
  function Page (line 18) | function Page() {
  function Page (line 35) | function Page() {
  function Comp (line 54) | function Comp() {
  function Page (line 59) | function Page() {
  function Page (line 86) | function Page() {
  function Page (line 123) | function Page() {
  function Page (line 160) | function Page() {
  function Page (line 199) | function Page() {

FILE: test/use-swr-promise.test.tsx
  function Page (line 20) | function Page() {
  function Page (line 61) | function Page() {
  function Page (line 94) | function Page() {
  function Page (line 137) | function Page() {
  function Comp (line 173) | function Comp() {

FILE: test/use-swr-reconnect.test.tsx
  function Page (line 14) | function Page() {
  function Page (line 40) | function Page() {
  function Page (line 69) | function Page() {
  function Page (line 98) | function Page() {

FILE: test/use-swr-refresh.test.tsx
  function Page (line 22) | function Page() {
  function Page (line 50) | function Page() {
  function Page (line 81) | function Page() {
  function Page (line 140) | function Page() {
  function Page (line 208) | function Page() {
  function App (line 229) | function App() {
  function Page (line 275) | function Page() {
  function Page (line 510) | function Page() {
  function Page (line 564) | function Page() {
  function Page (line 619) | function Page() {
  function Page (line 647) | function Page() {
  function Page (line 684) | function Page() {
  function Page (line 720) | function Page() {

FILE: test/use-swr-remote-mutation.test.tsx
  function Page (line 13) | function Page() {
  function Page (line 32) | function Page() {
  function Page (line 61) | function Page() {
  function Page (line 88) | function Page() {
  function Page (line 112) | function Page() {
  function Page (line 139) | function Page() {
  function Page (line 173) | function Page() {
  function Page (line 206) | function Page() {
  function Page (line 235) | function Page() {
  function Page (line 275) | function Page() {
  function Page (line 302) | function Page() {
  function Page (line 330) | function Page() {
  function Page (line 345) | function Page() {
  function Page (line 373) | function Page() {
  function Page (line 408) | function Page() {
  function Page (line 433) | function Page() {
  function Page (line 458) | function Page() {
  function Page (line 501) | function Page() {
  function Page (line 543) | function Page() {
  function Page (line 589) | function Page() {
  function Page (line 633) | function Page() {
  function Page (line 680) | function Page() {
  function Page (line 737) | function Page() {
  function Page (line 770) | function Page() {
  function Page (line 807) | function Page() {
  function Page (line 839) | function Page() {
  function Page (line 859) | function Page() {
  function Page (line 913) | function Page() {
  function Page (line 945) | function Page() {
  function Page (line 989) | function Page() {
  function Page (line 1042) | function Page() {
  function Page (line 1088) | function Page() {
  function Page (line 1126) | function Page() {

FILE: test/use-swr-revalidate.test.tsx
  function Page (line 18) | function Page() {
  function Page (line 40) | function Page() {
  function Page (line 68) | function Page() {
  function Page (line 96) | function Page() {
  function Page (line 130) | function Page() {
  function Page (line 153) | function Page() {
  function Foo (line 171) | function Foo() {
  function Page (line 178) | function Page() {

FILE: test/use-swr-server.test.tsx
  function withServer (line 10) | async function withServer(runner: () => Promise<void>) {

FILE: test/use-swr-streaming-ssr.test.tsx
  function Block (line 24) | function Block() {
  function Block (line 45) | function Block({ suspense, delay, id }) {

FILE: test/use-swr-subscription.test.tsx
  function subscribe (line 14) | function subscribe(key, { next }) {
  function Page (line 28) | function Page() {
  function subscribe (line 71) | function subscribe(key, { next }) {
  function Page (line 85) | function Page() {
  function subscribe (line 127) | function subscribe(key, { next }) {
  function Page (line 133) | function Page() {
  function subscribe (line 164) | function subscribe(key, { next }) {
  function Page (line 182) | function Page() {
  function subscribe (line 208) | function subscribe(key, { next }) {
  function Page (line 225) | function Page() {
  function subscribe (line 301) | function subscribe() {
  function Page (line 305) | function Page() {

FILE: test/use-swr-suspense.test.tsx
  function Section (line 23) | function Section() {
  function Section (line 45) | function Section() {
  function Section (line 80) | function Section() {
  function Section (line 98) | function Section() {
  function Section (line 129) | function Section() {
  function Section (line 164) | function Section() {
  function Section (line 190) | function Section() {
  function Section (line 228) | function Section() {
  function Page (line 321) | function Page() {
  function Section (line 345) | function Section() {

FILE: test/utils.tsx
  function sleep (line 4) | function sleep(time: number) {
  function executeWithoutBatching (line 81) | async function executeWithoutBatching(fn: () => any) {
Condensed preview — 344 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (730K chars).
[
  {
    "path": ".codesandbox/ci.json",
    "chars": 180,
    "preview": "{\n  \"sandboxes\": [\"swr-basic-p7dg6\", \"swr-states-4une7\", \"swr-infinite-jb5bm\", \"swr-ssr-j9b2y\"],\n  \"node\": \"18\",\n  \"inst"
  },
  {
    "path": ".editorconfig",
    "chars": 69,
    "preview": "root = true\n\n[*.{js,ts,jsx,tsx}]\nindent_size = 2\nindent_style = space"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 22,
    "preview": "*    @shuding @huozhi\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 3338,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 2112,
    "preview": "# SWR Contribution Guidelines\n\nThank you for reading this guide and we appreciate any contribution.\n\n## Ask a Question\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 446,
    "preview": "---\nname: Bug report\nabout: Create a bug report for the SWR library\n---\n\n# Bug report\n\n## Description / Observed Behavio"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 183,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask Question\n    url: https://github.com/vercel/swr/discussions\n   "
  },
  {
    "path": ".github/SECURITY.md",
    "chars": 324,
    "preview": "# Reporting Security Issues\n\nIf you believe you have found a security vulnerability in SWR, we encourage you to let us k"
  },
  {
    "path": ".github/workflows/install/action.yml",
    "chars": 556,
    "preview": "name: 'Install'\ndescription: 'Set up and install dependencies'\nruns:\n  using: composite\n  steps:\n    - name: Setup pnpm\n"
  },
  {
    "path": ".github/workflows/test-canary.yml",
    "chars": 590,
    "preview": "name: Test React Canary\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 0 * * *'\n\njobs:\n  test:\n    runs-on: ubuntu"
  },
  {
    "path": ".github/workflows/test-legacy-react.yml",
    "chars": 466,
    "preview": "name: Test React 17\n\non:\n  push:\n    branches:\n      - main\n    tags:\n      - v*\n  pull_request:\n\njobs:\n  test:\n    runs"
  },
  {
    "path": ".github/workflows/test-release.yml",
    "chars": 2139,
    "preview": "name: Test and Release\n\non:\n  push:\n    branches:\n      - main\n    tags:\n      - v*\n  pull_request:\n\njobs:\n  test:\n    r"
  },
  {
    "path": ".github/workflows/trigger-release.yml",
    "chars": 1239,
    "preview": "on:\n  workflow_dispatch:\n    inputs:\n      releaseType:\n        description: Release stable or beta?\n        required: t"
  },
  {
    "path": ".gitignore",
    "chars": 185,
    "preview": "node_modules\ndist\n*.log\n*.tgz\n.env\n.next\n.DS_Store\n.idea\n.vscode\n.eslintcache\nexamples/**/yarn.lock\npackage-lock.json\n*."
  },
  {
    "path": ".husky/pre-commit",
    "chars": 37,
    "preview": "pnpm lint-staged && pnpm types:check\n"
  },
  {
    "path": ".npmrc",
    "chars": 99,
    "preview": "# prevent sub-packages from installing peer-deps (multiple react versions)\nauto-install-peers=false"
  },
  {
    "path": ".swcrc",
    "chars": 244,
    "preview": "{\n  \"$schema\": \"https://json.schemastore.org/swcrc\",\n  \"sourceMaps\": true,\n  \"jsc\": {\n    \"parser\": {\n      \"syntax\": \"t"
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2023 Vercel, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 3737,
    "preview": "[![SWR](https://assets.vercel.com/image/upload/v1572289618/swr/banner.png)](https://swr.vercel.app)\n\n<p align=\"center\">\n"
  },
  {
    "path": "_internal/package.json",
    "chars": 148,
    "preview": "{\n  \"main\": \"../dist/_internal/index.js\",\n  \"module\": \"../dist/_internal/index.mjs\",\n  \"types\": \"../dist/_internal/index"
  },
  {
    "path": "e2e/site/README.md",
    "chars": 1748,
    "preview": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js"
  },
  {
    "path": "e2e/site/app/basic-ssr/block.tsx",
    "chars": 446,
    "preview": "'use client'\n\nimport useSWR from 'swr'\nimport { useDebugHistory } from '~/lib/use-debug-history'\n\nexport default functio"
  },
  {
    "path": "e2e/site/app/basic-ssr/page.tsx",
    "chars": 91,
    "preview": "import Block from './block'\n\nexport default function BasicSSRPage() {\n  return <Block />\n}\n"
  },
  {
    "path": "e2e/site/app/concurrent-transition/page.tsx",
    "chars": 686,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport dynamic from 'next/dynamic'\n\nconst TransitionDemo = dynamic(() => "
  },
  {
    "path": "e2e/site/app/concurrent-transition/transition-demo.tsx",
    "chars": 1755,
    "preview": "'use client'\n\nimport React, { useState, useTransition, Suspense, useCallback } from 'react'\nimport useSWR from 'swr'\n\n//"
  },
  {
    "path": "e2e/site/app/issue-2702/page.tsx",
    "chars": 126,
    "preview": "import Comp from './reproduction'\n\nexport default function Page() {\n  return (\n    <div>\n      <Comp></Comp>\n    </div>\n"
  },
  {
    "path": "e2e/site/app/issue-2702/reproduction.tsx",
    "chars": 780,
    "preview": "'use client'\nimport useSWR, { preload } from 'swr'\nimport { Suspense, use, useEffect, useState } from 'react'\n\nconst sle"
  },
  {
    "path": "e2e/site/app/layout.tsx",
    "chars": 377,
    "preview": "export default function RootLayout({\n  children\n}: {\n  children: React.ReactNode\n}) {\n  return (\n    <html lang=\"en\">\n  "
  },
  {
    "path": "e2e/site/app/mutate-server-action/action.tsx",
    "chars": 260,
    "preview": "'use server'\n\nexport async function action(): Promise<{ result: number }> {\n  await sleep(500)\n  return { result: 10086 "
  },
  {
    "path": "e2e/site/app/mutate-server-action/page.tsx",
    "chars": 484,
    "preview": "'use client'\nimport useSWRMutation from 'swr/mutation'\nimport { action } from './action'\n\nconst useServerActionMutation "
  },
  {
    "path": "e2e/site/app/page.tsx",
    "chars": 824,
    "preview": "'use client'\n\nimport { Suspense, useReducer } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '"
  },
  {
    "path": "e2e/site/app/partially-hydrate/layout.tsx",
    "chars": 521,
    "preview": "'use client'\nimport type { PropsWithChildren } from 'react'\nimport { useDebugHistory } from '~/lib/use-debug-history'\nim"
  },
  {
    "path": "e2e/site/app/partially-hydrate/loading.tsx",
    "chars": 69,
    "preview": "export default function Loading() {\n  return <div>Loading...</div>\n}\n"
  },
  {
    "path": "e2e/site/app/partially-hydrate/page.tsx",
    "chars": 598,
    "preview": "'use client'\nimport { useDebugHistory } from '~/lib/use-debug-history'\nimport useData from './use-data'\nimport { use } f"
  },
  {
    "path": "e2e/site/app/partially-hydrate/use-data.tsx",
    "chars": 204,
    "preview": "import useSWR from 'swr'\n\nexport default function useData() {\n  return useSWR<string>('/api/data', async (url: string) ="
  },
  {
    "path": "e2e/site/app/perf/page.tsx",
    "chars": 1379,
    "preview": "'use client'\nimport { useState } from 'react'\nimport useSWR from 'swr'\n\nconst elementCount = 10_000\nconst useData = () ="
  },
  {
    "path": "e2e/site/app/react-server-entry/page.tsx",
    "chars": 434,
    "preview": "import { unstable_serialize } from 'swr'\nimport { unstable_serialize as infinite_unstable_serialize } from 'swr/infinite"
  },
  {
    "path": "e2e/site/app/render-count/page.tsx",
    "chars": 188,
    "preview": "'use client'\nimport useSWR from 'swr'\n\nexport default function Page() {\n  useSWR('swr should not cause extra rerenders')"
  },
  {
    "path": "e2e/site/app/render-preload-avoid-waterfall/page.tsx",
    "chars": 1380,
    "preview": "'use client'\n\nimport { Suspense, useEffect, useState } from 'react'\nimport useSWR, { preload } from 'swr'\nimport { OnlyR"
  },
  {
    "path": "e2e/site/app/render-preload-basic/page.tsx",
    "chars": 1159,
    "preview": "'use client'\n\nimport { Suspense, useEffect, useState } from 'react'\nimport useSWR, { preload } from 'swr'\nimport { OnlyR"
  },
  {
    "path": "e2e/site/app/render-promise-suspense-error/page.tsx",
    "chars": 1266,
    "preview": "'use client'\n\nimport { Suspense, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { ErrorB"
  },
  {
    "path": "e2e/site/app/render-promise-suspense-resolve/page.tsx",
    "chars": 1359,
    "preview": "'use client'\n\nimport { Suspense, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport useSWR, "
  },
  {
    "path": "e2e/site/app/render-promise-suspense-shared/page.tsx",
    "chars": 1141,
    "preview": "'use client'\n\nimport { Suspense, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport useSWR, "
  },
  {
    "path": "e2e/site/app/render-suspense-avoid-rerender/page.tsx",
    "chars": 1180,
    "preview": "'use client'\n\nimport { Suspense, useRef } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/co"
  },
  {
    "path": "e2e/site/app/render-suspense-cached-error/page.tsx",
    "chars": 1268,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport { ErrorBoundary } from 'react-error-boundary'\nimport useSWR, { SWR"
  },
  {
    "path": "e2e/site/app/render-suspense-error/page.tsx",
    "chars": 928,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport useSWR from 'swr'\nimport { ErrorBoundary } from 'react-error-bound"
  },
  {
    "path": "e2e/site/app/render-suspense-fallback/page.tsx",
    "chars": 634,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/component/"
  },
  {
    "path": "e2e/site/app/render-suspense-fetcher/page.tsx",
    "chars": 1400,
    "preview": "'use client'\n\nimport { Suspense, useRef, useState } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } "
  },
  {
    "path": "e2e/site/app/render-suspense-infinite-preload/page.tsx",
    "chars": 1534,
    "preview": "'use client'\n\nimport { Suspense, useEffect, useState } from 'react'\nimport useSWRInfinite from 'swr/infinite'\nimport { p"
  },
  {
    "path": "e2e/site/app/render-suspense-initial-data/page.tsx",
    "chars": 463,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport useSWR from 'swr'\n\nconst fetcher = () => 'SWR'\n\nfunction PageConte"
  },
  {
    "path": "e2e/site/app/render-suspense-keep-previous/page.tsx",
    "chars": 1217,
    "preview": "'use client'\n\nimport { Suspense, useState } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/"
  },
  {
    "path": "e2e/site/app/render-suspense-key-change/page.tsx",
    "chars": 900,
    "preview": "'use client'\n\nimport { Suspense, useState } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/"
  },
  {
    "path": "e2e/site/app/render-suspense-multiple-fallbacks/page.tsx",
    "chars": 897,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/component/"
  },
  {
    "path": "e2e/site/app/render-suspense-no-revalidate/page.tsx",
    "chars": 1035,
    "preview": "'use client'\n\nimport { Suspense, useLayoutEffect, useState } from 'react'\nimport useSWR, { preload } from 'swr'\nimport {"
  },
  {
    "path": "e2e/site/app/render-suspense-non-promise/page.tsx",
    "chars": 523,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/component/"
  },
  {
    "path": "e2e/site/app/render-suspense-null-key/page.tsx",
    "chars": 1453,
    "preview": "'use client'\n\nimport { Suspense, useState } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/"
  },
  {
    "path": "e2e/site/app/render-suspense-same-data/page.tsx",
    "chars": 900,
    "preview": "'use client'\n\nimport { Suspense, useState } from 'react'\nimport useSWR from 'swr'\nimport { OnlyRenderInClient } from '~/"
  },
  {
    "path": "e2e/site/app/server-prefetch-warning/page.tsx",
    "chars": 923,
    "preview": "'use client'\n\nimport { useEffect, useState } from 'react'\nimport useSWR, { SWRConfig } from 'swr'\n\nconst fetcher = () =>"
  },
  {
    "path": "e2e/site/app/suspense-after-preload/page.tsx",
    "chars": 317,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport dynamic from 'next/dynamic'\n\nconst RemoteData = dynamic(() => impo"
  },
  {
    "path": "e2e/site/app/suspense-after-preload/remote-data.tsx",
    "chars": 865,
    "preview": "'use client'\nimport { Suspense, useState } from 'react'\nimport useSWR from 'swr'\nimport { preload } from 'swr'\n\nconst fe"
  },
  {
    "path": "e2e/site/app/suspense-fallback/layout.tsx",
    "chars": 433,
    "preview": "import { SWRConfig } from 'swr'\n\nfunction createPromiseData(data: any, timeout: number) {\n  return new Promise(resolve ="
  },
  {
    "path": "e2e/site/app/suspense-fallback/promise/page.tsx",
    "chars": 190,
    "preview": "'use client'\n\nimport useSWR from 'swr'\n\nexport default function Page() {\n  const { data, isLoading } = useSWR('/api/prom"
  },
  {
    "path": "e2e/site/app/suspense-infinite-get-key/page.tsx",
    "chars": 1072,
    "preview": "'use client'\n\nimport { Suspense, useState } from 'react'\nimport useSWRInfinite from 'swr/infinite'\nimport { OnlyRenderIn"
  },
  {
    "path": "e2e/site/app/suspense-retry/manual-retry.tsx",
    "chars": 855,
    "preview": "'use client'\nimport { Suspense } from 'react'\nimport { ErrorBoundary } from 'react-error-boundary'\nimport { useRemoteDat"
  },
  {
    "path": "e2e/site/app/suspense-retry/page.tsx",
    "chars": 318,
    "preview": "'use client'\n\nimport { Suspense } from 'react'\nimport dynamic from 'next/dynamic'\n\nconst RemoteData = dynamic(() => impo"
  },
  {
    "path": "e2e/site/app/suspense-retry/use-remote-data.ts",
    "chars": 422,
    "preview": "'use client'\nimport useSWR from 'swr'\nimport { preload } from 'swr'\n\nlet count = 0\nconst fetcher = () => {\n  count++\n  i"
  },
  {
    "path": "e2e/site/app/suspense-undefined-key/page.tsx",
    "chars": 723,
    "preview": "'use client'\n\nimport { Suspense, useReducer } from 'react'\nimport useSWR from 'swr'\n\nconst fetcher = async (key: string)"
  },
  {
    "path": "e2e/site/component/manual-retry-mutate.tsx",
    "chars": 1101,
    "preview": "import { Suspense } from 'react'\nimport { ErrorBoundary } from 'react-error-boundary'\nimport useSWR from 'swr'\nimport { "
  },
  {
    "path": "e2e/site/component/manual-retry.tsx",
    "chars": 842,
    "preview": "import { Suspense } from 'react'\nimport { ErrorBoundary } from 'react-error-boundary'\nimport { useRemoteData, preloadRem"
  },
  {
    "path": "e2e/site/component/only-render-in-client.tsx",
    "chars": 383,
    "preview": "'use client'\nimport React from 'react'\n\n// This component ensures its children are only rendered on the client side.\nexp"
  },
  {
    "path": "e2e/site/component/use-remote-data.ts",
    "chars": 416,
    "preview": "import useSWR from 'swr'\nimport { preload } from 'swr'\n\nlet count = 0\nexport const fetcher = () => {\n  count++\n  if (cou"
  },
  {
    "path": "e2e/site/lib/sleep.ts",
    "chars": 101,
    "preview": "export function sleep(ms: number) {\n  return new Promise<void>(resolve => setTimeout(resolve, ms))\n}\n"
  },
  {
    "path": "e2e/site/lib/use-debug-history.ts",
    "chars": 407,
    "preview": "'use client'\nimport { useEffect, useRef } from 'react'\n\nexport const useDebugHistory = <T>(data: T, prefix = '') => {\n  "
  },
  {
    "path": "e2e/site/next-env.d.ts",
    "chars": 312,
    "preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n/// <reference types=\"next/navigation-t"
  },
  {
    "path": "e2e/site/next.config.js",
    "chars": 92,
    "preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {}\n\nmodule.exports = nextConfig\n"
  },
  {
    "path": "e2e/site/package.json",
    "chars": 433,
    "preview": "{\n  \"name\": \"site\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next bu"
  },
  {
    "path": "e2e/site/pages/api/data.ts",
    "chars": 308,
    "preview": "// Next.js API route support: https://nextjs.org/docs/api-routes/introduction\nimport type { NextApiRequest, NextApiRespo"
  },
  {
    "path": "e2e/site/pages/api/retry.ts",
    "chars": 245,
    "preview": "import type { NextApiRequest, NextApiResponse } from 'next'\n\ntype Data = {\n  name: string\n}\n\nexport default function han"
  },
  {
    "path": "e2e/site/pages/suspense-retry-19.tsx",
    "chars": 315,
    "preview": "import { Suspense } from 'react'\nimport dynamic from 'next/dynamic'\n\nconst RemoteData = dynamic(() => import('../compone"
  },
  {
    "path": "e2e/site/pages/suspense-retry-mutate.tsx",
    "chars": 322,
    "preview": "import { Suspense } from 'react'\nimport dynamic from 'next/dynamic'\n\nconst RemoteData = dynamic(() => import('../compone"
  },
  {
    "path": "e2e/site/tsconfig.json",
    "chars": 758,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    "
  },
  {
    "path": "e2e/test/concurrent-transition.test.ts",
    "chars": 1383,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest.describe('concurrent rendering transitions', () => {\n  test('shoul"
  },
  {
    "path": "e2e/test/initial-render.test.ts",
    "chars": 2587,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest.describe('rendering', () => {\n  test('suspense with preload', asyn"
  },
  {
    "path": "e2e/test/issue-2702-too-many-hooks.ts",
    "chars": 487,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest.describe('issue 2702', () => {\n  test('should not crash with too m"
  },
  {
    "path": "e2e/test/mutate-server-action.test.ts",
    "chars": 487,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest('mutate-server-action', async ({ page }) => {\n  await page.goto('."
  },
  {
    "path": "e2e/test/perf.test.ts",
    "chars": 3888,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest.describe('performance', () => {\n  test('should render expensive co"
  },
  {
    "path": "e2e/test/preload-scenarios.test.ts",
    "chars": 802,
    "preview": "import { expect, test } from '@playwright/test'\n\ntest.describe('preload scenarios', () => {\n  test('preloads fetcher bef"
  },
  {
    "path": "e2e/test/promise-scenarios.test.ts",
    "chars": 1552,
    "preview": "import { expect, test } from '@playwright/test'\n\ntest.describe('promise scenarios', () => {\n  test('suspends while resol"
  },
  {
    "path": "e2e/test/server-prefetch-warning.test.ts",
    "chars": 1070,
    "preview": "import { test, expect } from '@playwright/test'\n\nconst warningForKey = (key: string) =>\n  `Missing pre-initiated data fo"
  },
  {
    "path": "e2e/test/stream-ssr.test.ts",
    "chars": 1689,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest.describe('Stream SSR', () => {\n  test('Basic SSR', async ({ page }"
  },
  {
    "path": "e2e/test/suspense-fallback.test.ts",
    "chars": 332,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest.describe('suspense fallback', () => {\n  test('should wait for prom"
  },
  {
    "path": "e2e/test/suspense-scenarios.test.ts",
    "chars": 7369,
    "preview": "import { expect, test } from '@playwright/test'\n\n// Group all suspense-related scenarios so they run together.\ntest.desc"
  },
  {
    "path": "e2e/test/suspense-undefined-key.test.ts",
    "chars": 699,
    "preview": "import { test, expect } from '@playwright/test'\n\ntest.describe('suspense with undefined key', () => {\n  test('should ren"
  },
  {
    "path": "e2e/test/tsconfig.json",
    "chars": 68,
    "preview": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\n    \".\",\n  ],\n}"
  },
  {
    "path": "eslint.config.mjs",
    "chars": 2915,
    "preview": "import { defineConfig, globalIgnores } from 'eslint/config'\nimport typescriptEslint from '@typescript-eslint/eslint-plug"
  },
  {
    "path": "examples/.eslintrc",
    "chars": 107,
    "preview": "// next is loading eslintrc from the root directory, adding this to avoid eslint rules being overridden\n{}\n"
  },
  {
    "path": "examples/api-hooks/README.md",
    "chars": 629,
    "preview": "# API Hooks\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.com/bu"
  },
  {
    "path": "examples/api-hooks/hooks/use-projects.js",
    "chars": 140,
    "preview": "import useSWR from 'swr'\n\nimport fetch from '../libs/fetch'\n\nexport default function useProjects() {\n  return useSWR('/a"
  },
  {
    "path": "examples/api-hooks/hooks/use-repository.js",
    "chars": 152,
    "preview": "import useSWR from 'swr'\n\nimport fetch from '../libs/fetch'\n\nexport default function useRepository(id) {\n  return useSWR"
  },
  {
    "path": "examples/api-hooks/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/api-hooks/package.json",
    "chars": 299,
    "preview": "{\n  \"name\": \"api-hooks\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"next\":"
  },
  {
    "path": "examples/api-hooks/pages/[user]/[repo].js",
    "chars": 590,
    "preview": "import Link from 'next/link'\nimport useRepository from '../../hooks/use-repository'\n\nexport default function Repo() {\n  "
  },
  {
    "path": "examples/api-hooks/pages/api/data.js",
    "chars": 459,
    "preview": "const projects = [\n  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'\n]\n\nexport default (req, re"
  },
  {
    "path": "examples/api-hooks/pages/index.js",
    "chars": 445,
    "preview": "import Link from 'next/link'\nimport useProjects from '../hooks/use-projects'\n\nexport default function Index() {\n  const "
  },
  {
    "path": "examples/autocomplete-suggestions/README.md",
    "chars": 621,
    "preview": "# Autocomplete Suggestions\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https:"
  },
  {
    "path": "examples/autocomplete-suggestions/libs/fetcher.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/autocomplete-suggestions/package.json",
    "chars": 379,
    "preview": "{\n  \"name\": \"autocomplete-suggestions\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\""
  },
  {
    "path": "examples/autocomplete-suggestions/pages/api/suggestions.js",
    "chars": 3578,
    "preview": "const countries = [\n  \"United States\",\n  \"Canada\",\n  \"United Kingdom\",\n  \"Argentina\",\n  \"Australia\",\n  \"Austria\",\n  \"Bel"
  },
  {
    "path": "examples/autocomplete-suggestions/pages/index.js",
    "chars": 1268,
    "preview": "import { useMemo, useState } from \"react\"\nimport fetcher from '../libs/fetcher'\nimport {\n  Combobox,\n  ComboboxInput,\n  "
  },
  {
    "path": "examples/axios/README.md",
    "chars": 549,
    "preview": "# Axios\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.com/button"
  },
  {
    "path": "examples/axios/libs/useRequest.js",
    "chars": 412,
    "preview": "import useSWR from 'swr'\nimport axios from 'axios'\n\nexport default function useRequest(request, { fallbackData, ...confi"
  },
  {
    "path": "examples/axios/package.json",
    "chars": 318,
    "preview": "{\n  \"name\": \"basic\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"axios\": \"0"
  },
  {
    "path": "examples/axios/pages/[user]/[repo].js",
    "chars": 736,
    "preview": "import Link from 'next/link'\n\nimport useRequest from '../../libs/useRequest'\n\nexport default function Repo() {\n  const i"
  },
  {
    "path": "examples/axios/pages/api/data.js",
    "chars": 480,
    "preview": "import axios from 'axios'\n\nconst projects = [\n  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'"
  },
  {
    "path": "examples/axios/pages/index.js",
    "chars": 552,
    "preview": "import Link from 'next/link'\n\nimport useRequest from '../libs/useRequest'\n\nexport default function Index() {\n  const { d"
  },
  {
    "path": "examples/axios-typescript/README.md",
    "chars": 642,
    "preview": "# Axios TypeScript\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel"
  },
  {
    "path": "examples/axios-typescript/libs/useRequest.ts",
    "chars": 1598,
    "preview": "import useSWR, { SWRConfiguration, SWRResponse } from 'swr'\nimport axios, { AxiosRequestConfig, AxiosResponse, AxiosErro"
  },
  {
    "path": "examples/axios-typescript/next-env.d.ts",
    "chars": 245,
    "preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/types/global\" />\n/// <reference types=\"next/image-types/global"
  },
  {
    "path": "examples/axios-typescript/package.json",
    "chars": 443,
    "preview": "{\n  \"name\": \"basic-typescript\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    "
  },
  {
    "path": "examples/axios-typescript/pages/[user]/[repo].tsx",
    "chars": 729,
    "preview": "import Link from 'next/link'\n\nimport useRequest from '../../libs/useRequest'\n\nexport default function Repo() {\n  const i"
  },
  {
    "path": "examples/axios-typescript/pages/api/data.ts",
    "chars": 595,
    "preview": "import { NextApiRequest, NextApiResponse } from 'next'\nimport axios from 'axios'\n\nconst projects = [\n  'facebook/flipper"
  },
  {
    "path": "examples/axios-typescript/pages/index.tsx",
    "chars": 562,
    "preview": "import Link from 'next/link'\n\nimport useRequest from '../libs/useRequest'\n\nexport default function Index() {\n  const { d"
  },
  {
    "path": "examples/axios-typescript/tsconfig.json",
    "chars": 532,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    "
  },
  {
    "path": "examples/basic/README.md",
    "chars": 556,
    "preview": "# Basic\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.com/button"
  },
  {
    "path": "examples/basic/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/basic/package.json",
    "chars": 270,
    "preview": "{\n  \"name\": \"basic\",\n  \"private\": true,\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"next\": \"latest\",\n    \"react\": \"late"
  },
  {
    "path": "examples/basic/pages/[user]/[repo].js",
    "chars": 616,
    "preview": "import Link from 'next/link'\nimport fetch from '../../libs/fetch'\n\nimport useSWR from 'swr'\n\nexport default function Rep"
  },
  {
    "path": "examples/basic/pages/api/data.js",
    "chars": 468,
    "preview": "const projects = [\n  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'\n]\n\nexport default function"
  },
  {
    "path": "examples/basic/pages/index.js",
    "chars": 470,
    "preview": "import Link from 'next/link'\nimport fetch from '../libs/fetch'\n\nimport useSWR from 'swr'\n\nexport default function Index("
  },
  {
    "path": "examples/basic-typescript/README.md",
    "chars": 616,
    "preview": "# Basic TypeScript\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel"
  },
  {
    "path": "examples/basic-typescript/libs/fetch.ts",
    "chars": 174,
    "preview": "export default async function fetcher<JSON = any>(\n  input: RequestInfo,\n  init?: RequestInit\n): Promise<JSON> {\n  const"
  },
  {
    "path": "examples/basic-typescript/next-env.d.ts",
    "chars": 245,
    "preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/types/global\" />\n/// <reference types=\"next/image-types/global"
  },
  {
    "path": "examples/basic-typescript/package.json",
    "chars": 420,
    "preview": "{\n  \"name\": \"basic-typescript\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    "
  },
  {
    "path": "examples/basic-typescript/pages/[user]/[repo].tsx",
    "chars": 721,
    "preview": "import Link from 'next/link'\nimport fetch from '../../libs/fetch'\n\nimport useSWR from 'swr'\n\nexport default function Rep"
  },
  {
    "path": "examples/basic-typescript/pages/api/data.ts",
    "chars": 563,
    "preview": "import { NextApiRequest, NextApiResponse } from 'next'\n\nconst projects = [\n  'facebook/flipper',\n  'vuejs/vuepress',\n  '"
  },
  {
    "path": "examples/basic-typescript/pages/index.tsx",
    "chars": 628,
    "preview": "import Link from 'next/link'\nimport fetch from '../libs/fetch'\n\nimport useSWR from 'swr'\n\nexport default function HomePa"
  },
  {
    "path": "examples/basic-typescript/tsconfig.json",
    "chars": 532,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    "
  },
  {
    "path": "examples/focus-revalidate/README.md",
    "chars": 659,
    "preview": "# Focus Revalidate\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel"
  },
  {
    "path": "examples/focus-revalidate/components/button.js",
    "chars": 350,
    "preview": "export default function Button({ children, ...props }) {\n  return <div><button {...props}>\n    {children}\n    <style jsx"
  },
  {
    "path": "examples/focus-revalidate/libs/auth.js",
    "chars": 204,
    "preview": "// mock login and logout\n\nexport function login() {\n  document.cookie = 'swr-test-token=swr;'\n}\n\nexport function logout("
  },
  {
    "path": "examples/focus-revalidate/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/focus-revalidate/package.json",
    "chars": 306,
    "preview": "{\n  \"name\": \"focus-revalidate\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    "
  },
  {
    "path": "examples/focus-revalidate/pages/api/user.js",
    "chars": 311,
    "preview": "// an endpoint for getting user info\nexport default function user(req, res) {\n  if (req.cookies['swr-test-token'] === 's"
  },
  {
    "path": "examples/focus-revalidate/pages/index.js",
    "chars": 701,
    "preview": "import Button from '../components/button'\nimport fetch from '../libs/fetch'\nimport { login, logout } from '../libs/auth'"
  },
  {
    "path": "examples/global-fetcher/README.md",
    "chars": 604,
    "preview": "# Global Fetcher\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.c"
  },
  {
    "path": "examples/global-fetcher/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/global-fetcher/package.json",
    "chars": 304,
    "preview": "{\n  \"name\": \"global-fetcher\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"n"
  },
  {
    "path": "examples/global-fetcher/pages/[user]/[repo].js",
    "chars": 572,
    "preview": "import Link from 'next/link'\n\nimport useSWR from 'swr'\n\nexport default function Repo() {\n  const id = typeof window !== "
  },
  {
    "path": "examples/global-fetcher/pages/_app.js",
    "chars": 359,
    "preview": "import React from 'react'\nimport App from 'next/app'\nimport { SWRConfig } from 'swr'\nimport fetch from '../libs/fetch.js"
  },
  {
    "path": "examples/global-fetcher/pages/api/data.js",
    "chars": 468,
    "preview": "const projects = [\n  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'\n]\n\nexport default function"
  },
  {
    "path": "examples/global-fetcher/pages/index.js",
    "chars": 514,
    "preview": "import Link from 'next/link'\n\nimport useSWR from 'swr'\n\nexport default function Index() {\n  const { data } = useSWR('/ap"
  },
  {
    "path": "examples/infinite/README.md",
    "chars": 551,
    "preview": "# useSWRInfinite\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.c"
  },
  {
    "path": "examples/infinite/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/infinite/package.json",
    "chars": 298,
    "preview": "{\n  \"name\": \"infinite\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"next\": "
  },
  {
    "path": "examples/infinite/pages/index.js",
    "chars": 2093,
    "preview": "import { useState } from 'react'\nimport useSWRInfinite from 'swr/infinite'\n\nimport fetch from '../libs/fetch'\n\nconst PAG"
  },
  {
    "path": "examples/infinite-scroll/README.md",
    "chars": 641,
    "preview": "# useSWRInfinite with scroll based on IntersectionObserver\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel"
  },
  {
    "path": "examples/infinite-scroll/hooks/useOnScreen.js",
    "chars": 497,
    "preview": "import { useState, useEffect } from 'react'\n\nexport default function useOnScreen(ref) {\n  const [isIntersecting, setInte"
  },
  {
    "path": "examples/infinite-scroll/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/infinite-scroll/package.json",
    "chars": 305,
    "preview": "{\n  \"name\": \"infinite-scroll\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \""
  },
  {
    "path": "examples/infinite-scroll/pages/index.js",
    "chars": 2341,
    "preview": "import useSWRInfinite from 'swr/infinite'\nimport { useState, useRef, useEffect } from 'react'\n\nimport fetcher from '../l"
  },
  {
    "path": "examples/local-state-sharing/README.md",
    "chars": 588,
    "preview": "# Basic\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.com/button"
  },
  {
    "path": "examples/local-state-sharing/libs/store.js",
    "chars": 69,
    "preview": "const initialStore = { name: \"john\" };\n\nexport default initialStore;\n"
  },
  {
    "path": "examples/local-state-sharing/package.json",
    "chars": 309,
    "preview": "{\n  \"name\": \"local-state-sharing\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n "
  },
  {
    "path": "examples/local-state-sharing/pages/index.js",
    "chars": 1208,
    "preview": "import { useState } from \"react\"\nimport initialStore from \"../libs/store\"\nimport useSWR, { mutate } from \"swr\"\n\nfunction"
  },
  {
    "path": "examples/optimistic-ui/README.md",
    "chars": 666,
    "preview": "# Optimistic UI\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.co"
  },
  {
    "path": "examples/optimistic-ui/libs/fetch.js",
    "chars": 156,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  if (!res.ok) throw new Error('Fail"
  },
  {
    "path": "examples/optimistic-ui/package.json",
    "chars": 303,
    "preview": "{\n  \"name\": \"optimistic-ui\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"ne"
  },
  {
    "path": "examples/optimistic-ui/pages/_app.js",
    "chars": 120,
    "preview": "import '../styles.css'\n\nexport default function App({ Component, pageProps }) {\n  return <Component {...pageProps} />\n}\n"
  },
  {
    "path": "examples/optimistic-ui/pages/api/todos.js",
    "chars": 802,
    "preview": "let todos = []\nconst delay = () => new Promise(res => setTimeout(() => res(), 1000))\n\nasync function getTodos() {\n  awai"
  },
  {
    "path": "examples/optimistic-ui/pages/index.js",
    "chars": 3585,
    "preview": "import useSWR from 'swr'\nimport React, { useState } from 'react'\n\nimport fetch from '../libs/fetch'\n\nexport default func"
  },
  {
    "path": "examples/optimistic-ui/styles.css",
    "chars": 1251,
    "preview": "html {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,\n    Ubuntu, Cantarell, 'Open Sans',"
  },
  {
    "path": "examples/optimistic-ui-immer/README.md",
    "chars": 705,
    "preview": "# Optimistic UI with Immer\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https:"
  },
  {
    "path": "examples/optimistic-ui-immer/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/optimistic-ui-immer/package.json",
    "chars": 331,
    "preview": "{\n  \"name\": \"optimistic-ui-immer\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n "
  },
  {
    "path": "examples/optimistic-ui-immer/pages/api/data.js",
    "chars": 404,
    "preview": "const data = []\n\nfunction shouldFail() {\n  return Math.random() > 0.8\n}\n\nexport default function api(req, res) {\n  if (r"
  },
  {
    "path": "examples/optimistic-ui-immer/pages/index.js",
    "chars": 1471,
    "preview": "import React from 'react'\nimport fetch from '../libs/fetch'\n\nimport useSWR, { mutate } from 'swr'\nimport produce from \"i"
  },
  {
    "path": "examples/prefetch-preload/README.md",
    "chars": 1046,
    "preview": "# Prefetch & Preload\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://verc"
  },
  {
    "path": "examples/prefetch-preload/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/prefetch-preload/package.json",
    "chars": 306,
    "preview": "{\n  \"name\": \"prefetch-preload\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    "
  },
  {
    "path": "examples/prefetch-preload/pages/[user]/[repo].js",
    "chars": 1304,
    "preview": "import Head from \"next/head\"\nimport Link from 'next/link'\nimport React from 'react'\nimport fetch from '../../libs/fetch'"
  },
  {
    "path": "examples/prefetch-preload/pages/api/data.js",
    "chars": 468,
    "preview": "const projects = [\n  'facebook/flipper', 'vuejs/vuepress', 'rust-lang/rust', 'vercel/next.js'\n]\n\nexport default function"
  },
  {
    "path": "examples/prefetch-preload/pages/index.js",
    "chars": 1898,
    "preview": "import React from 'react'\nimport Head from \"next/head\";\nimport Link from 'next/link'\nimport fetch from '../libs/fetch'\n\n"
  },
  {
    "path": "examples/refetch-interval/README.md",
    "chars": 636,
    "preview": "# Refetch Interval\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel Now.\n\n[![Deploy with Vercel Now](https:"
  },
  {
    "path": "examples/refetch-interval/components/button.js",
    "chars": 350,
    "preview": "export default function Button({ children, ...props }) {\n  return <div><button {...props}>\n    {children}\n    <style jsx"
  },
  {
    "path": "examples/refetch-interval/libs/fetch.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/refetch-interval/package.json",
    "chars": 306,
    "preview": "{\n  \"name\": \"refetch-interval\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    "
  },
  {
    "path": "examples/refetch-interval/pages/api/data.js",
    "chars": 223,
    "preview": "// an simple endpoint for getting current list\nlet list = []\n\nexport default function api(req, res) {\n  if (req.query.ad"
  },
  {
    "path": "examples/refetch-interval/pages/index.js",
    "chars": 956,
    "preview": "import { useState } from 'react'\nimport Button from '../components/button'\nimport fetch from '../libs/fetch'\n\nimport use"
  },
  {
    "path": "examples/server-render/README.md",
    "chars": 908,
    "preview": "# Server Render\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.co"
  },
  {
    "path": "examples/server-render/libs/fetcher.js",
    "chars": 106,
    "preview": "export default async function fetcher(...args) {\n  const res = await fetch(...args)\n  return res.json()\n}\n"
  },
  {
    "path": "examples/server-render/package.json",
    "chars": 303,
    "preview": "{\n  \"name\": \"server-render\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"ne"
  },
  {
    "path": "examples/server-render/pages/[pokemon].js",
    "chars": 1012,
    "preview": "import Link from 'next/link'\nimport fetcher from '../libs/fetcher'\n\nimport useSWR from 'swr'\n\nconst getURL = pokemon => "
  },
  {
    "path": "examples/server-render/pages/index.js",
    "chars": 795,
    "preview": "import Link from 'next/link'\nimport fetcher from '../libs/fetcher'\n\nimport useSWR from 'swr'\n\nconst URL = 'https://pokea"
  },
  {
    "path": "examples/storage-tab-sync/README.md",
    "chars": 600,
    "preview": "# Storage Tab Sync\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel"
  },
  {
    "path": "examples/storage-tab-sync/libs/storage.js",
    "chars": 147,
    "preview": "export default async function storage(key) {\n  const value = localStorage.getItem(key)\n  if (!value) return undefined\n  "
  },
  {
    "path": "examples/storage-tab-sync/package.json",
    "chars": 306,
    "preview": "{\n  \"name\": \"storage-tab-sync\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    "
  },
  {
    "path": "examples/storage-tab-sync/pages/index.js",
    "chars": 556,
    "preview": "import storage from '../libs/storage'\n\nimport useSWR, { mutate } from 'swr'\n\nexport default function Index() {\n  const {"
  },
  {
    "path": "examples/subscription/README.md",
    "chars": 585,
    "preview": "# Subscription\n\n## One-Click Deploy\n\nDeploy your own SWR project with Vercel.\n\n[![Deploy with Vercel](https://vercel.com"
  },
  {
    "path": "examples/subscription/package.json",
    "chars": 277,
    "preview": "{\n  \"name\": \"subscription\",\n  \"private\": true,\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"next\": \"latest\",\n    \"react\""
  },
  {
    "path": "examples/subscription/pages/index.js",
    "chars": 978,
    "preview": "import React from \"react\"\nimport useSWRSubscription from \"swr/subscription\"\nimport EventEmitter from \"events\"\n\nconst eve"
  }
]

// ... and 144 more files (download for full content)

About this extraction

This page contains the full source code of the vercel/swr GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 344 files (663.2 KB), approximately 188.2k tokens, and a symbol index with 762 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!