SEO & Loader questions

I have the following code snippit:

import { safe } from "@orpc/client";
import { createFileRoute, useRouter } from "@tanstack/react-router";
import { useEffect } from "react";
import { toast } from "sonner";
import { orpc } from "@/orpc/client";

export const Route = createFileRoute("/blog/$blogId")({
    component: RouteComponent,
    loader: async ({ params }) => {
        const { data, error } = await safe(
            orpc.getBlogPost.call({ slug: params.blogId }),
        );
        if (error) {
            throw error;
        }

        return { blogPost: data };
    },
    errorComponent: (error) => <BlogPostErrorComponent error={error} />,
    // errorComponent: BlogPostErrorComponent({ error })
});

function BlogPostErrorComponent({ error }: { error: unknown }) {
    const router = useRouter();

    useEffect(() => {
        toast.error(
            (error as Error)?.message ||
                "An error occurred while fetching the blog post.",
        );

        // Redirect to the blog page
        router.navigate({ to: "/blog" });
    });

    return null;
}

function RouteComponent() {
    const { blogPost } = Route.useLoaderData();

    return <div> {JSON.stringify(blogPost)}</div>;
}


questions about this snippet:
1. Is this the best way to handle rerouting while also showing something in the UI? I am using oRPC, and while I can redirect in the loader, I can't render anything (Because from what I understand, the loader runs on the client and server, and so it isn't possible to access the DOM in this function)

And on this snippet:
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { Suspense } from "react";
import { orpc } from "@/orpc/client";

export const Route = createFileRoute("/blog/")({
    component: Blog,
    loader: async ({ context }) => {
        context.queryClient.ensureQueryData(
            orpc.getBlogPosts.queryOptions({
                input: {},
                queryKey: ["blogPosts"],
                staleTime: 1000 * 60 * 5, // 5 minutes
                cacheTime: 1000 * 60 * 10, // 10 minutes
            }),
        );
    },
});

function BlogSkeleton() {
    return <div>Loading...</div>;
}

function BlogComponent() {
    const allBlogPosts = useSuspenseQuery(
        orpc.getBlogPosts.queryOptions({
            input: {},
            queryKey: ["blogPosts"],
            staleTime: 1000 * 60 * 5, // 5 minutes
            cacheTime: 1000 * 60 * 10, // 10 minutes
        }),
    );

    return (
        <div>
            <h1>Blog</h1>
            {JSON.stringify(allBlogPosts.data)}
        </div>
    );
}

function Blog() {
    return (
        <div>
            <Suspense fallback={<BlogSkeleton />}>
                <BlogComponent />
            </Suspense>
        </div>
    );
}


Why does the blog never get stored in cache? Or, why do I always see the Loading Div when I refresh or get routed back from the Error component of the splat group?
Whats the difference between ensureQueryData and prefetchQuery? (Esp in the context of vvvv)

For both snippets, is this an OK approach for SEO? I am not sure if the engines would see the loading divs or will wait for the data to be streamed (? Assuming thats the right terminology) before moving on.

Thanks for the help.
Was this page helpful?