--- title: UI-Patterns order: 6 --- # UI Patterns > This page is incomplete. You can help us expanding it by suggesting more patterns or asking us about common problems you're facing on [GitHub Discussions](https://github.com/urql-graphql/urql/discussions). Generally, `urql`'s API surface is small and compact. Some common problems that we're facing when building apps may look like they're not a built-in feature, however, there are several patterns that even a lean UI can support. This page is a collection of common UI patterns and problems we may face with GraphQL and how we can tackle them in `urql`. These examples will be written in React but apply to any other framework. ## Infinite scrolling "Infinite Scrolling" is the approach of loading more data into a page's list without splitting that list up across multiple pages. There are a few ways of going about this. In our [normalized caching chapter on the topic](../graphcache/local-resolvers.md#pagination) we see an approach with `urql`'s normalized cache, which is suitable to get started quickly. However, this approach also requires some UI code as well to keep track of pages. Let's have a look at how we can create a UI implementation that makes use of this normalized caching feature. ```js import React from 'react'; import { useQuery, gql } from 'urql'; const PageQuery = gql` query Page($first: Int!, $after: String) { todos(first: $first, after: $after) { nodes { id name } pageInfo { hasNextPage endCursor } } } `; const SearchResultPage = ({ variables, isLastPage, onLoadMore }) => { const [{ data, fetching, error }] = useQuery({ query: PageQuery, variables }); const todos = data?.todos; return (
{error &&

Oh no... {error.message}

} {fetching &&

Loading...

} {todos && ( <> {todos.nodes.map(todo => (
{todo.id}: {todo.name}
))} {isLastPage && todos.pageInfo.hasNextPage && ( )} )}
); }; const Search = () => { const [pageVariables, setPageVariables] = useState([ { first: 10, after: '', }, ]); return (
{pageVariables.map((variables, i) => ( setPageVariables([...pageVariables, { after, first: 10 }])} /> ))}
); }; ``` Here we keep an array of all `variables` we've encountered and use them to render their respective `result` page. This only rerenders the additional page rather than having a long list that constantly changes. [You can find a full code example of this pattern in our example folder on the topic of pagination.](https://github.com/urql-graphql/urql/tree/main/examples/with-pagination) This code doesn't take changing variables into account, which will affect the cursors. For an example that takes full infinite scrolling into account, [you can find a full code example of an extended pattern in our example folder on the topic of infinite pagination.](https://github.com/urql-graphql/urql/tree/main/examples/with-infinite-pagination) ## Prefetching data We sometimes find it necessary to load data for a new page before that page is opened, for instance while a JS bundle is still loading. We may do this with help of the `Client`, by calling methods without using the React bindings directly. ```js import React from 'react'; import { useClient, gql } from 'urql'; const TodoQuery = gql` query Todo($id: ID!) { todo(id: $id) { id name } } `; const Component = () => { const client = useClient(); const router = useRouter(); const transitionPage = React.useCallback(async id => { const loadJSBundle = import('./page.js'); const loadData = client.query(TodoQuery, { id }).toPromise(); await Promise.all([loadJSBundle, loadData]); router.push(`/todo/${id}`); }, []); return ; }; ``` Here we're calling `client.query` to prepare a query when the transition begins. We then call `toPromise()` on this query which activates it. Our `Client` and its cache share results, which means that we've already kicked off or even completed the query before we're on the new page. ## Lazy query It's often required to "lazily" start a query, either at a later point or imperatively. This means that we don't start a query when a new component is mounted immediately. Parts of `urql` that automatically start, like the `useQuery` hook, have a concept of a [`pause` option.](./react-preact.md#pausing-usequery) This option is used to prevent the hook from automatically starting a new query. ```js import React from 'react'; import { useQuery, gql } from 'urql'; const TodoQuery = gql` query Todos { todos { id name } } `; const Component = () => { const [result, fetch] = useQuery({ query: TodoQuery, pause: true }); const router = useRouter(); return ; }; ``` We can unpause the hook to start fetching, or, like in this example, call its returned function to manually kick off the query. ## Reacting to focus and stale time In urql we leverage our extensibility pattern named "Exchanges" to manipulate the way data comes in and goes out of our client. - [Stale time](https://github.com/urql-graphql/urql/tree/main/exchanges/request-policy) - [Focus](https://github.com/urql-graphql/urql/tree/main/exchanges/refocus) When we want to introduce one of these patterns we add the package and add it to the `exchanges` property of our `Client`. In the case of these two we'll have to add it before the cache else our requests will never get upgraded. ```js import { Client, cacheExchange, fetchExchange } from 'urql'; import { refocusExchange } from '@urql/exchange-refocus'; const client = new Client({ url: 'some-url', exchanges: [refocusExchange(), cacheExchange, fetchExchange], }); ``` That's all we need to do to react to these patterns.