Skip to content

TypeScript

SWRV is friendly for apps written in TypeScript, with type safety out of the box.

TIP

The snippets on this page focus on types. Assume they live inside <script setup lang="ts"> unless a snippet is showing a standalone helper type or function.

Basic usage

useSWRV

By default, SWRV also infers the argument types of fetcher from key:

ts
// `key` is inferred as `string`
useSWRV("/api/user", async (key) => key);
useSWRV(
  () => "/api/user",
  async (key) => key,
);
ts
// `key` is inferred as the object shape
useSWRV({ a: "1", b: { c: "3", d: 2 } }, async (key) => key);
useSWRV(
  () => ({ a: "1", b: { c: "3", d: 2 } }),
  async (key) => key,
);
ts
// tuple keys are spread into fetcher arguments in SWRV
useSWRV(["user", 8] as const, async (arg0, arg1) => ({ arg0, arg1 }));
useSWRV(
  () => ["user", 8] as const,
  async (arg0, arg1) => ({ arg0, arg1 }),
);

You can also explicitly specify the types for key and the fetcher:

ts
import useSWRV, { type Fetcher } from "swrv";

const uid = "<user_id>";
const fetcher: Fetcher<User, string> = (id) => getUserById(id);

const { data } = useSWRV(uid, fetcher);
// `data` is `Ref<User | undefined>`.

By default, the error thrown inside fetcher is unknown. You can also specify it explicitly:

ts
const { data, error } = useSWRV<User, Error>(uid, fetcher);
// `data` is `Ref<User | undefined>`.
// `error` is `Ref<Error | undefined>`.

useSWRVInfinite

The same applies to swrv/infinite:

ts
import useSWRVInfinite from "swrv/infinite";
import type { SWRVInfiniteKeyLoader } from "swrv";

type Page = { data: string[]; nextCursor: string | null };

const getKey: SWRVInfiniteKeyLoader<Page> = (index, previousPageData) => {
  if (previousPageData && !previousPageData.data.length) {
    return null;
  }

  return `/api/projects?page=${index}`;
};

const { data } = useSWRVInfinite(getKey, fetcher);

useSWRVSubscription

Inline subscribe functions can infer the key type too:

ts
import useSWRVSubscription from "swrv/subscription";
import type { SWRVSubscriptionOptions } from "swrv";

const { data, error } = useSWRVSubscription(
  "key",
  (key, { next }: SWRVSubscriptionOptions<number, Error>) => {
    key satisfies string;
    next(undefined, 1);
    return () => {};
  },
);

You can also type the subscribe function itself:

ts
import useSWRVSubscription from "swrv/subscription";
import type { SWRVSubscription } from "swrv";

const subscribe: SWRVSubscription<string, number, Error> = (key, { next }) => {
  key satisfies string;
  next(undefined, 1);
  return () => {};
};

const { data, error } = useSWRVSubscription("key", subscribe);

Generics

Specifying the data type is easy. In many cases, the fetcher type is enough:

ts
const getUser = async (url: string) => {
  const response = await fetch(url);
  return response.json() as Promise<{ id: string; name: string }>;
};

const response = useSWRV("/api/user", getUser);

If the fetcher is less specific, pass the data type explicitly:

ts
const response = useSWRV<{ id: string; name: string }>("/api/user", fetcher);

fallbackData also narrows data.value for that composable call:

ts
const response = useSWRV("/api/user", fetcher, {
  fallbackData: { id: "loading", name: "Loading" },
});

response.data.value.id;

If the fetcher comes from SWRVConfig, useSWRV(key, config) also works:

ts
const response = useSWRV<{ id: string }>("/api/user", {
  fallbackData: { id: "1" },
});

useSWRVMutation preserves trigger argument typing for required args, optional args, and no-arg triggers:

ts
const mutation = useSWRVMutation("/api/user", async (_key, { arg }: { arg: { name: string } }) => ({
  id: "1",
  name: arg.name,
}));

void mutation.trigger({ name: "Ada" });

If you want to type a reusable config object, import SWRVConfiguration directly:

ts
import type { SWRVConfiguration } from "swrv";

const config: SWRVConfiguration<string[]> = {
  fallbackData: ["loading"],
  revalidateOnMount: false,
};

const { data } = useSWRV("/api/data", fetcher, config);

Middleware types

There are also extra type definitions you can import for custom middleware and config boundaries:

  • Fetcher
  • BareFetcher
  • RawKey
  • KeySource
  • SWRVConfiguration
  • SWRVMiddleware
  • MutatorOptions

Most application code can rely on inference and only reach for explicit types at shared fetchers, composable libraries, or middleware boundaries.

For example:

ts
import type { SWRVMiddleware } from "swrv";

const swrvMiddleware: SWRVMiddleware = (useSWRVNext) => (key, fetcher, config) => {
  return useSWRVNext(key, fetcher, config);
};

Released under the Apache-2.0 License.