API Reference

injectInfiniteQuery

Call Signature

ts
function injectInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(injectInfiniteQueryFn, options?): DefinedCreateInfiniteQueryResult<TData, TError>;

Defined in: inject-infinite-query.ts:83

The options for injectInfiniteQuery are identical to injectQuery, with the addition of initialPageParam, getNextPageParam, getPreviousPageParam, and maxPages. Infinite queries can additively "load more" data onto an existing set of data, or "infinite scroll".

This overload is selected when initialData is set on the options returned by injectInfiniteQueryFn, so the resulting data signal is never undefined (unless a select changes TData to include undefined).

Type Parameters

TQueryFnData

TQueryFnData

TError

TError = Error

TData

TData = InfiniteData<TQueryFnData, unknown>

TQueryKey

TQueryKey extends readonly unknown[] = readonly unknown[]

TPageParam

TPageParam = unknown

Parameters

injectInfiniteQueryFn

() => DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>

A function returning the DefinedInitialDataInfiniteOptions to use — everything you can pass to injectInfiniteQuery, with initialData set. Similar to computed from Angular, this function runs in the reactive context, so signals read inside it drive the query.

options?

InjectInfiniteQueryOptions

Additional configuration.

Returns

DefinedCreateInfiniteQueryResult<TData, TError>

The same signals as injectQuery, with the addition of fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage, isFetchingNextPage, and isFetchingPreviousPage. data().pages and data().pageParams are also added, as long as a select doesn't change TData away from its default InfiniteData<TQueryFnData> shape.

Remarks

Keep in mind that imperative fetch calls, such as fetchNextPage, may interfere with the default refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user actions, or add conditions like hasNextPage() && !isFetching().

See

infiniteQueryOptions to share these options between injectInfiniteQuery and imperative APIs like queryClient.fetchInfiniteQuery.

Example

ts
@Component({
  selector: 'projects',
  template: `
    <!-- `projectsQuery.data()` is never `undefined`, thanks to `initialData` — even if a
    refetch fails, so the list stays visible alongside the error. -->
    <ul>
      @for (page of projectsQuery.data().pages; track $index) {
        @for (project of page.projects; track project.id) {
          <li>{{ project.name }}</li>
        }
      }
    </ul>
  `,
})
export class Projects {
  readonly projectsQuery = injectInfiniteQuery(() => ({
    queryKey: ['projects'],
    queryFn: ({ pageParam }) => fetchProjects(pageParam),
    initialPageParam: 0,
    getNextPageParam: (lastPage) => lastPage.nextId,
    initialData: { pages: [], pageParams: [] },
  }))
}

Call Signature

ts
function injectInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(injectInfiniteQueryFn, options?): CreateInfiniteQueryResult<TData, TError>;

Defined in: inject-infinite-query.ts:239

Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. Infinite queries can additively "load more" data onto an existing set of data, or "infinite scroll".

Type Parameters

TQueryFnData

TQueryFnData

TError

TError = Error

TData

TData = InfiniteData<TQueryFnData, unknown>

TQueryKey

TQueryKey extends readonly unknown[] = readonly unknown[]

TPageParam

TPageParam = unknown

Parameters

injectInfiniteQueryFn

() => UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>

A function returning the UndefinedInitialDataInfiniteOptions to use — everything you can pass to injectInfiniteQuery. Similar to computed from Angular, this function runs in the reactive context, so signals read inside it drive the query.

options?

InjectInfiniteQueryOptions

Additional configuration.

Returns

CreateInfiniteQueryResult<TData, TError>

The same signals as injectQuery, with the addition of fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage, isFetchingNextPage, and isFetchingPreviousPage. data().pages and data().pageParams are also added, as long as a select doesn't change TData away from its default InfiniteData<TQueryFnData> shape.

Remarks

Keep in mind that imperative fetch calls, such as fetchNextPage, may interfere with the default refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user actions, or add conditions like hasNextPage() && !isFetching(). This is the only overload that accepts queryFn: skipToken, shown below.

See

infiniteQueryOptions to share these options between injectInfiniteQuery and imperative APIs like queryClient.fetchInfiniteQuery.

Examples

Fetching the next page from a button click:

ts
@Component({
  selector: 'projects-list',
  template: `
    <ul>
      @for (page of projectsQuery.data()?.pages; track $index) {
        @for (project of page.projects; track project.id) {
          <li>{{ project.name }}</li>
        }
      }
    </ul>
    <button
      [disabled]="!projectsQuery.hasNextPage() || projectsQuery.isFetching()"
      (click)="projectsQuery.fetchNextPage()"
    >
      Load More
    </button>
  `,
})
export class ProjectsList {
  readonly projectsQuery = injectInfiniteQuery(() => ({
    queryKey: ['projects'],
    queryFn: ({ pageParam }) => fetchProjects(pageParam),
    initialPageParam: 0,
    getNextPageParam: (lastPage) => lastPage.nextId,
  }))
}

Fetching the next page automatically as the user scrolls, using an IntersectionObserver on a sentinel element after the list:

ts
@Component({
  selector: 'projects-list',
  template: `
    <ul>
      @for (page of projectsQuery.data()?.pages; track $index) {
        @for (project of page.projects; track project.id) {
          <li>{{ project.name }}</li>
        }
      }
    </ul>
    <div #sentinel>{{ projectsQuery.isFetchingNextPage() ? 'Loading more...' : '' }}</div>
  `,
})
export class ProjectsList {
  readonly sentinel = viewChild<ElementRef<HTMLElement>>('sentinel')

  readonly projectsQuery = injectInfiniteQuery(() => ({
    queryKey: ['projects'],
    queryFn: ({ pageParam }) => fetchProjects(pageParam),
    initialPageParam: 0,
    getNextPageParam: (lastPage) => lastPage.nextId,
  }))

  constructor() {
    effect((onCleanup) => {
      const sentinel = this.sentinel()?.nativeElement
      if (
        sentinel == null ||
        !this.projectsQuery.hasNextPage() ||
        this.projectsQuery.isFetching()
      ) {
        return
      }

      const observer = new IntersectionObserver(([entry]) => {
        if (entry?.isIntersecting) this.projectsQuery.fetchNextPage()
      })
      observer.observe(sentinel)

      onCleanup(() => observer.disconnect())
    })
  }
}

A query that's disabled, type safe, until postId is set — pass skipToken as queryFn instead of setting enabled: false:

ts
@Component({
  selector: 'comments',
  template: `
    @if (postId() == null) {
      Select a post
    } @else if (commentsQuery.isPending()) {
      Loading...
    } @else if (commentsQuery.isError()) {
      <span>Error: {{ commentsQuery.error()?.message }}</span>
    } @else {
      <ul>
        @for (page of commentsQuery.data().pages; track $index) {
          @for (comment of page.comments; track comment.id) {
            <li>{{ comment.text }}</li>
          }
        }
      </ul>
    }
  `,
})
export class Comments {
  readonly postId = signal<string | undefined>(undefined)

  readonly commentsQuery = injectInfiniteQuery(() => ({
    queryKey: ['post', this.postId(), 'comments'],
    queryFn:
      this.postId() != null
        ? ({ pageParam }) => fetchComments(this.postId()!, pageParam)
        : skipToken,
    initialPageParam: 0,
    getNextPageParam: (lastPage) => lastPage.nextId,
  }))
}

Call Signature

ts
function injectInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(injectInfiniteQueryFn, options?): CreateInfiniteQueryResult<TData, TError>;

Defined in: inject-infinite-query.ts:267

This overload accepts the general CreateInfiniteQueryOptions shape rather than the initialData-aware overloads above, so whether data is defined can't be inferred from the call site — useful when wrapping injectInfiniteQuery in your own helper function that forwards caller-provided options.

Type Parameters

TQueryFnData

TQueryFnData

TError

TError = Error

TData

TData = InfiniteData<TQueryFnData, unknown>

TQueryKey

TQueryKey extends readonly unknown[] = readonly unknown[]

TPageParam

TPageParam = unknown

Parameters

injectInfiniteQueryFn

() => CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>

A function that returns infinite query options. Similar to computed from Angular, this function runs in the reactive context, so signals read inside it drive the query.

options?

InjectInfiniteQueryOptions

Additional configuration.

Returns

CreateInfiniteQueryResult<TData, TError>

The infinite query result.