Check if Sanity post exists in React Router v5/v6 before rendering

7 replies
Last updated: Jan 28, 2026
How can we check inside React Router V5 or V6 if a Sanity.io [type="post"] Exists? Currently, the NotFound component will not work it always returns a empty page.

const App = () => {

return (

<BrowserRouter>

<Routes>

<Route element={<AllPosts />} path="/" exact />

<Route element={<BlogSinglePost />} path="/blog/:slug" />

<Route element={<NotFound />} path="*" />

</Routes>

</BrowserRouter>

);

};
AI Update

The issue you're experiencing is that your BlogSinglePost component needs to check if the document exists and handle the case when it doesn't. React Router will always match the /blog/:slug route before the * wildcard route, so you need to handle the 404 logic inside your BlogSinglePost component itself.

Here's how to fix this:

In your BlogSinglePost component:

import { useParams, Navigate } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { client } from './sanityClient'; // your Sanity client

const BlogSinglePost = () => {
  const { slug } = useParams();
  const [post, setPost] = useState(null);
  const [loading, setLoading] = useState(true);
  const [notFound, setNotFound] = useState(false);

  useEffect(() => {
    const query = `*[_type == "post" && slug.current == $slug][0]`;
    
    client.fetch(query, { slug })
      .then((data) => {
        if (!data) {
          setNotFound(true);
        } else {
          setPost(data);
        }
        setLoading(false);
      })
      .catch((error) => {
        console.error('Error fetching post:', error);
        setNotFound(true);
        setLoading(false);
      });
  }, [slug]);

  if (loading) return <div>Loading...</div>;
  if (notFound) return <Navigate to="/404" replace />;

  return (
    <div>
      {/* Your post content */}
      <h1>{post.title}</h1>
      {/* ... */}
    </div>
  );
};

Update your routes:

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route element={<AllPosts />} path="/" exact />
        <Route element={<BlogSinglePost />} path="/blog/:slug" />
        <Route element={<NotFound />} path="/404" />
        <Route element={<NotFound />} path="*" />
      </Routes>
    </BrowserRouter>
  );
};

Key points:

  1. The GROQ query with parameters *[_type == "post" && slug.current == $slug][0] filters documents by type and slug, using parameters for safe value passing
  2. The [0] at the end returns only the first match (or null if no match exists)
  3. When client.fetch returns null, you know the document doesn't exist
  4. Use React Router's <Navigate> component to redirect to your NotFound route when the post doesn't exist
  5. The wildcard * route will catch any other unmatched routes

This approach ensures that the document existence check happens at the data-fetching level, which is where it needs to be since React Router doesn't know anything about your Sanity content.

Show original thread
7 replies

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?