# Fetch Sanity Content https://www.sanity.io/learn/course/content-driven-web-application-foundations/fetch-sanity-content.md Query for your content using Sanity Client, a library compatible with the Next.js cache and React Server Components for modern, integrated data fetching. Sanity content is typically queried with GROQ queries from a configured [Sanity Client](https://www.sanity.io/docs/js-client). Fortunately, one has already been created for you. 1. **Open** `src/sanity/lib/client.ts` to confirm it exists in your project. Sanity Client is built to run in any JavaScript run time and in any framework. It is also compatible with Next.js caching features, React Server Components, and the App Router. It also provides ways to interact with Sanity projects and even write content back to the Content Lake with mutations. You'll use some of these features in later lessons. It's time to put everything we've set up to work. In this lesson, you'll create a route to serve as a Post index page and a dynamic route to display an individual post. ## Next.js App Router For now, you'll focus on data fetching at the top of each route. React Server Components allow you to perform fetches from inside individual components. Future lessons may address where this is beneficial. For now, our queries are simple enough – and GROQ is expressive enough – to get everything we need at the top of the tree. 1. See the [Next.js App Router](https://nextjs.org/docs/app/building-your-application/routing) documentation for more details about file-based routing and how file and folder names impact URLs The most significant change we'll make first is creating a separate "Route Group" for the entire application front end. This route group will separate the front end layout code from the Studio without affecting the URL. It is also useful when integrating Visual Editing and displaying the front end _inside_ the Studio. 1. **Create** a new `(frontend)` directory and **duplicate** `layout.tsx` into it ```sh mkdir -p "src/app/(frontend)" && cp "src/app/layout.tsx" "src/app/(frontend)/" ``` You should now have **two** `layout.tsx` files inside the app folder at these locations: ```text src └── app ├── // all other files ├── layout.tsx └── (frontend) └── layout.tsx ``` The `(frontend)/layout.tsx` file has duplicated `html` and `body` tags, but you'll update the file those later in the lesson. 1. **Update** the root `layout.tsx` file to remove `globals.css` ## Update the home page Later in this track, the home page will become fully featured. For now, it just needs a link to the posts index. 1. **Move** `page.tsx` into the `(frontend)` folder 2. **Update** your home page route to add basic navigation to the posts index. ```tsx:src/app/(frontend)/page.tsx import Link from "next/link"; export default async function Page() { return (

Home


Posts index →
); } ``` 1. Next.js provides the [`` component](https://nextjs.org/docs/pages/api-reference/components/link) as an enhancement to the [HTML anchor](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) (``) element. You should now have a basic home page like this: ![Basic home page](https://cdn.sanity.io/images/3do82whm/next/6f35be43157c8edf9d8c478c5e9b8f39743f0a6a-2144x1388.png) ## Create a post-index page This page will list up to 12 of the latest post documents. Inside this route: * The configured Sanity Client is imported as `client` * The GROQ query `POSTS_QUERY` is used by `client.fetch` * Thanks to automatic type inference, the response will be typed `POSTS_QUERYResult` 1. **Create** a new directory for a post-index page to fetch all `post` type documents ```tsx:src/app/(frontend)/posts/page.tsx import Link from "next/link"; import { client } from "@/sanity/lib/client"; import { POSTS_QUERY } from "@/sanity/lib/queries"; const options = { next: { revalidate: 60 } }; export default async function Page() { const posts = await client.fetch(POSTS_QUERY, {}, options); return (

Post index


← Return home
); } ``` 1. Next.js supports [React Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components), which allow you to fetch and `await` data within the component. Read more on the Next.js documentation. ### Viewing the post-index You should now have a post index page at [http://localhost:3000/posts](http://localhost:3000/posts) like this: ![Blog posts index web page](https://cdn.sanity.io/images/3do82whm/next/f7951171b80f0ed0024178315cb49450ca6c1e75-2144x1388.png) ### Current cache configuration The `options` variable passed into the Sanity Client is a light configuration for Next.js caching. You should know that with these settings, the cache has been configured to only update pages at most every 60 seconds. Finding the right balance between fresh and stale content is a complex topic, and there are ways to mitigate the concerns of your content creators and end users to find a solution for everyone. If you'd like to learn more on the topic and continue to configure caching manually, see: [Controlling cached content in Next.js](https://www.sanity.io/learn/course/controlling-cached-content-in-next-js). What's better than manually configuring the cache? **Never doing it.** ## Live by default The `next-sanity` package contains helper functions to perform fetches that take advantage of the [Live Content API](https://www.sanity.io/learn/content-lake/live-content-api). So every fetch for data is automatically cached and revalidated using the built-in tag-based revalidation. 1. **Update** the frontend `layout.tsx` file to include `SanityLive` ```tsx:src/app/(frontend)/layout.tsx import { SanityLive } from '@/sanity/lib/live' export default function FrontendLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( <> {children} ) } ``` 1. **Update** the post index page's fetch from `client` to `sanityFetch` ```tsx:src/app/(frontend)/posts/page.tsx import Link from "next/link"; import { sanityFetch } from "@/sanity/lib/live"; import { POSTS_QUERY } from "@/sanity/lib/queries"; export default async function Page() { const { data: posts } = await sanityFetch({ query: POSTS_QUERY }); return (

Post index

    {posts.map((post) => (
  • {post?.title}
  • ))}

← Return home
); } ``` Now when you publish changes in Sanity Studio, you should see those updates take place live. No more caching. No more hammering the refresh button. ### Sanity TypeGen in Beta The GROQ query included a filter to ensure only documents with a `slug.current` was defined – but the TypeGen generated a type where `slug.current` could be `null`. This is a known limitation of TypeGen while it is in beta. ## Create an individual post page The GROQ query `POST_QUERY` used a variable `$slug` to match a route with a `post` in the dataset. For this, you can use a "Dynamic Route," where a segment in the URL is made available to the server component for the route as a prop. 1. Read more about [Next.js Dynamic Routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) on their documentation So, for example, because you're creating a route at: ```text src/app/(frontend)/posts/[slug]/page.tsx ``` If you visited the URL: ```text http://localhost:3000/posts/hello-world ``` The route would have this `params` object in its `props`: ```json { "slug": "hello-world" } ``` Which can then be passed into Sanity Client to match the value of `slug` to a value in a document. 1. **Create** a new route for an individual post ```tsx:src/app/(frontend)/posts/[slug]/page.tsx import { sanityFetch } from "@/sanity/lib/live"; import { POST_QUERY } from "@/sanity/lib/queries"; import { notFound } from "next/navigation"; import Link from "next/link"; export default async function Page({ params, }: { params: Promise<{ slug: string }>; }) { const { data: post } = await sanityFetch({ query: POST_QUERY, params: await params, }); if (!post) { notFound(); } return (

{post?.title}


← Return to index
); } ``` You should now be able to click any of the links on the posts index page and see the title of a blog post with a link back to the index: ![Individual post page showing just the title](https://cdn.sanity.io/images/3do82whm/next/14545936d26cac969adafd662cda50621ddd1ded-2144x1388.png) You now have a basic – but functional – web application. It's currently trapped in your local development environment. And while it isn't much, it's an excellent habit to deploy early and often so you can get into a habit of continuous improvement. You'll deploy your web application to the world in the following lessons.