11import { useNonce } from '@shopify/hydrogen' ;
2- import {
3- defer ,
4- type SerializeFrom ,
5- type LoaderFunctionArgs ,
6- } from '@shopify/remix-oxygen' ;
2+ import { defer , type LoaderFunctionArgs } from '@shopify/remix-oxygen' ;
73import {
84 Links ,
95 Meta ,
106 Outlet ,
117 Scripts ,
12- LiveReload ,
13- useMatches ,
8+ useRouteLoaderData ,
149 useRouteError ,
1510 useLoaderData ,
1611 ScrollRestoration ,
1712 isRouteErrorResponse ,
1813 type ShouldRevalidateFunction ,
1914} from '@remix-run/react' ;
2015import type { CustomerAccessToken } from '@shopify/hydrogen/storefront-api-types' ;
16+ import type {
17+ CartApiQueryFragment ,
18+ FooterQuery ,
19+ HeaderQuery ,
20+ RootMetaObjectQuery ,
21+ } from 'storefrontapi.generated' ;
2122import favicon from '../public/favicon.svg' ;
22- import resetStyles from './styles/reset.css' ;
23- import appStyles from './styles/app.css' ;
23+ import './styles/reset.css' ;
24+ import './styles/app.css' ;
25+
2426import { Layout } from '~/components/Layout' ;
2527
2628/**
@@ -46,9 +48,8 @@ export const shouldRevalidate: ShouldRevalidateFunction = ({
4648
4749export function links ( ) {
4850 return [
49- ...( cssBundleHref ? [ { rel : 'stylesheet' , href : cssBundleHref } ] : [ ] ) ,
50- { rel : 'stylesheet' , href : resetStyles } ,
51- { rel : 'stylesheet' , href : appStyles } ,
51+ { rel : 'stylesheet' , href : '/app/styles/reset.css' } ,
52+ { rel : 'stylesheet' , href : '/app/styles/app.css' } ,
5253 {
5354 rel : 'preconnect' ,
5455 href : 'https://cdn.shopify.com' ,
@@ -57,13 +58,34 @@ export function links() {
5758 rel : 'preconnect' ,
5859 href : 'https://shop.app' ,
5960 } ,
60- { rel : 'icon' , type : 'image/svg+xml' , href : favicon } ,
61+ { rel : 'icon' , type : 'image/svg+xml' , href : favicon } ,
6162 ] ;
6263}
6364
65+
66+ /**
67+ * Remix `SerializeFrom` loses top-level keys on `defer()` payloads; this matches runtime loader data.
68+ */
69+ export type RootLoaderClientData = {
70+ cart : Promise < CartApiQueryFragment | null > ;
71+ footer : Promise < FooterQuery > ;
72+ header : HeaderQuery ;
73+ isLoggedIn : boolean ;
74+ publicStoreDomain : string ;
75+ footerMetaObject : RootMetaObjectQuery ;
76+ } ;
77+
78+ const FALLBACK_HEADER : HeaderQuery = {
79+ shop : {
80+ id : 'gid://shopify/Shop/0' ,
81+ name : '' ,
82+ description : '' ,
83+ primaryDomain : { url : '' } ,
84+ } ,
85+ } ;
86+
6487export const useRootLoaderData = ( ) => {
65- const [ root ] = useMatches ( ) ;
66- return root ?. data as SerializeFrom < typeof loader > ;
88+ return useRouteLoaderData ( 'root' ) as unknown as RootLoaderClientData | undefined ;
6789} ;
6890
6991export async function loader ( { context} : LoaderFunctionArgs ) {
@@ -112,7 +134,7 @@ export async function loader({context}: LoaderFunctionArgs) {
112134
113135export default function App ( ) {
114136 const nonce = useNonce ( ) ;
115- const data = useLoaderData < typeof loader > ( ) ;
137+ const data = useLoaderData < typeof loader > ( ) as unknown as RootLoaderClientData ;
116138 return (
117139 < html lang = "en" >
118140 < head >
@@ -122,12 +144,17 @@ export default function App() {
122144 < Links />
123145 </ head >
124146 < body >
125- < Layout footerMetaObject = { data ?. footerMetaObject } { ...data } >
147+ < Layout
148+ cart = { data . cart }
149+ footer = { data . footer }
150+ header = { data . header }
151+ isLoggedIn = { data . isLoggedIn }
152+ footerMetaObject = { data . footerMetaObject }
153+ >
126154 < Outlet />
127155 </ Layout >
128156 < ScrollRestoration nonce = { nonce } />
129157 < Scripts nonce = { nonce } />
130- < LiveReload nonce = { nonce } />
131158 </ body >
132159 </ html >
133160 ) ;
@@ -156,7 +183,14 @@ export function ErrorBoundary() {
156183 < Links />
157184 </ head >
158185 < body >
159- < Layout { ...rootData } >
186+ < Layout
187+ fetchData = { rootData }
188+ cart = { rootData ?. cart ?? Promise . resolve ( null ) }
189+ footer = { rootData ?. footer ?? Promise . resolve ( { } as FooterQuery ) }
190+ header = { rootData ?. header ?? FALLBACK_HEADER }
191+ isLoggedIn = { rootData ?. isLoggedIn ?? false }
192+ footerMetaObject = { rootData ?. footerMetaObject }
193+ >
160194 < div className = "route-error" >
161195 < h1 > Oops</ h1 >
162196 < h2 > { errorStatus } </ h2 >
@@ -169,7 +203,6 @@ export function ErrorBoundary() {
169203 </ Layout >
170204 < ScrollRestoration nonce = { nonce } />
171205 < Scripts nonce = { nonce } />
172- < LiveReload nonce = { nonce } />
173206 </ body >
174207 </ html >
175208 ) ;
@@ -282,7 +315,7 @@ const FOOTER_QUERY = `#graphql
282315` as const ;
283316
284317const FOOTER_META_OBJECT_QUERY = `#graphql
285- query MetaObject ($country: CountryCode, $language: LanguageCode)
318+ query RootMetaObject ($country: CountryCode, $language: LanguageCode)
286319@inContext(country: $country, language: $language) {
287320 metaobjects(first: 100, type: "footer") {
288321 nodes {
0 commit comments