import {Await, useLoaderData, Link} from 'react-router'; import type {Route} from './+types/_index'; import {Suspense} from 'react'; import {Image} from '@shopify/hydrogen'; import type { FeaturedCollectionFragment, RecommendedProductsQuery, } from 'storefrontapi.generated'; import {ProductItem} from '~/components/ProductItem'; import {MockShopNotice} from '~/components/MockShopNotice'; export const meta: Route.MetaFunction = () => { return [{title: 'Hydrogen | Home'}]; }; export async function loader(args: Route.LoaderArgs) { // Start fetching non-critical data without blocking time to first byte const deferredData = loadDeferredData(args); // Await the critical data required to render initial state of the page const criticalData = await loadCriticalData(args); return {...deferredData, ...criticalData}; } /** * Load data necessary for rendering content above the fold. This is the critical data * needed to render the page. If it's unavailable, the whole page should 400 or 500 error. */ async function loadCriticalData({context}: Route.LoaderArgs) { const [{collections}] = await Promise.all([ context.storefront.query(FEATURED_COLLECTION_QUERY), // Add other queries here, so that they are loaded in parallel ]); return { isShopLinked: Boolean(context.env.PUBLIC_STORE_DOMAIN), featuredCollection: collections.nodes[0], }; } /** * Load data for rendering content below the fold. This data is deferred and will be * fetched after the initial page load. If it's unavailable, the page should still 200. * Make sure to not throw any errors here, as it will cause the page to 500. */ function loadDeferredData({context}: Route.LoaderArgs) { const recommendedProducts = context.storefront .query(RECOMMENDED_PRODUCTS_QUERY) .catch((error: Error) => { // Log query errors, but don't throw them so the page can still render console.error(error); return null; }); return { recommendedProducts, }; } export default function Homepage() { const data = useLoaderData(); return (
{data.isShopLinked ? null : }
); } function FeaturedCollection({ collection, }: { collection: FeaturedCollectionFragment; }) { if (!collection) return null; const image = collection?.image; return ( {image && (
{image.altText
)}

{collection.title}

); } function RecommendedProducts({ products, }: { products: Promise; }) { return (
Loading...}> {(response) => (
{response ? response.products.nodes.map((product) => ( )) : null}
)}

); } const FEATURED_COLLECTION_QUERY = `#graphql fragment FeaturedCollection on Collection { id title image { id url altText width height } handle } query FeaturedCollection($country: CountryCode, $language: LanguageCode) @inContext(country: $country, language: $language) { collections(first: 1, sortKey: UPDATED_AT, reverse: true) { nodes { ...FeaturedCollection } } } ` as const; const RECOMMENDED_PRODUCTS_QUERY = `#graphql fragment RecommendedProduct on Product { id title handle priceRange { minVariantPrice { amount currencyCode } } featuredImage { id url altText width height } } query RecommendedProducts ($country: CountryCode, $language: LanguageCode) @inContext(country: $country, language: $language) { products(first: 4, sortKey: UPDATED_AT, reverse: true) { nodes { ...RecommendedProduct } } } ` as const;