Introduction# When performing Static Site Generation (SSG) with Nuxt 4, you may want to load data from local JSON files to generate static pages. However, it is not as simple as Next.js’s getStaticProps, and there are several pitfalls to be aware of.
This article introduces the correct approach discovered through trial and error.
Problem: Why Simple fs Reading Doesn’t Work# First Approach (Failed)# c } o ; n i } c r ❌ s f o e t c c c c r n t T ( o o o o e C s u h f i n n n n t l t r i e m s s s s u i n s t p t t t t r e r c o n n e a d h r f p f d t s w o L t s a u a J p a e o . t l t S s o i s c m = h l a O i n t n a e P N d s ' l t a = a = . e e r t D a w t p e a . a a h f a = s w t s i w s r p o a e t a = . s a o r r i r e w n k = v i t p e ( a s e m a a d i e a r p i t d a t . s ) o m h F t j y r p . i a f s n { t o r l ) e o c ( r e e ; t n ' t s S c ( ( f ( o y h ) f s ' l n ( ; i ' p v c ` l ) a e ( / e ; t ( f d P h p u a a ' r l t t ) o l a h ; c P / : e a $ s t { s s h f t . , i r c l i w ' e n d u P g ( t a ) ) f t , - h = 8 } > ' ' ` p ) ) { u ; ; b l i c / d a t a ' , f i l e P a t h ) ; This approach has the following problems:
process.cwd() differs in build environments : The working directory differs between local development and build environments like VercelFiles cannot be found during Nitro pre-rendering : During SSG, Nitro operates in its own contextWithout useAsyncData, it also runs on the client side : This defeats the purpose of SSGSolution: Nitro Storage API + Server API Route + useAsyncData# Architecture# [ P S p [ D → D a e u G a u g r b e t N r e v l n a o i ↓ e ↓ i e n C r c r i J g o u u / a s S m s A s d t O S p e P e a e e N S o A I S t d m G n s t a b l e y R o / H e o B n n o r * T d a u t c u a . M d d i D t g j L e i l a e e s ] d n d t o g ] a ( n i / N n n a i e p t _ e i r p d / o a e l y d o S l c t o o a o a n l r d - a . t d g j h a e s e t o a A n c / P l [ I i . ) e . n . t p a s t i h d ] e )
e } x ) p n } r } ; n o i , o , u r t p } s } u x t r r , e ] t t o e c f r b d , e . d : r r a M v a i R c e e a i o e s r u ' o f { n w l u r e : l : n a d l O n A N e f u e L n t s a s { i l r i E s m : g t : n r p e e / p . k r u t : p { r t d { s o b s u e s e : r l : ' b r f : i d l e i t c [ a i n n r f / { t c d e u a d a e N e l a ' d r u , s t , a : x e a t t , a t C a ' r o s u n e f s i e } g r , ( v { e r a s s e t s v i a N i t r o S t o r a g e A P I Step 2: Create a Server API Route (Nitro Storage + fs Fallback)# i i e } m m x ) p p p c i } c i } c c i } c i } t ; s o o o o f o f o o f o f h e r r r n t n t n n c r n c r r r t t t s ( h s S ( h U A s C s T ( o e F s ( o e o v t ! r t e f r s c t o t r a n t a t e n t w e { { d p o c i o e c n y w s u l x s u r e p a w f u l w e s v s a t r l f i t r c / r r f a t i r e N s t e t t i n b s s n r a e e a t h c l i P c i s o r o o t d a P t d e p a s u h P r e t a r t r t r a d c a s a J a i d o l P a e P y t e r t a a r s t a k t S t S t / F l t a r a a : h a o h g f g e t a t : h y a O e l i v r a t t . t e e i e t o a n N E o l e d a m e h P i e S l K r r = ; R = c = . r c e e m ) E r n E t ' = e e i a e ( p r a S } f r = e c r o d y e g a a r f r a o l y i = { r v l r r a u p v e w d e s e r r - n f n o A e u o a t s a = e . a s P a s ( d c r e g r r n d r g a e t h i d o a d e { a , o E e ( r t e ( e ' S h f v a t i l t F ( t m v t { a s { t i i s r v h i d s a e e R y p ( A s o t l a I s e e ) l a t / x ' n o s . a ' s P t r o e t t c ( ) e t a [ i p t u t i t . t I o a P S e o t p S a t . s a H t a s h . a r g s a t m r l r { y ) u . t t a e t A ' t a e t t o ( a y o n ; s . s h n r u r t ) u g ( o h r s g c c C p S ' d P s r r ) s e ' r . a t e v e ( o a y ; l a C a a C a a r g o . i s f d t n e r o y v { o d s g e e r g a s s e h c r a d ( e d e s e p a e . P : ] ( m e p r e f e l A g t f c a . } a ( : a s : i t k a P e I s w t 4 t s e t a n s e c I K t d h 0 s f y v 4 h l 4 e : y e e e f ( , 4 r n e 0 P 0 d d ( y m o ) , o c n 0 a 0 a ( / ) ( r , ' m t , r , i t r \ ) s u m ( , a n a e / t d ' t e ' e m m m ' p / { o e p f s f v ' e ) e n ) l g r v u - s s e p s s u ; a , a e b 8 a ' n a s ? s x c g l l ' g ; t t a a t e ' e o i ) e ) h g p g . : K p c ; : ' e a e c / ' e m / = ) : t : o ) y e d ` > ; h n w ; ) n a F ' P ' f i ; t t i { P a I i t a l a r n g h e ' e t a v . n , h m a t : v n . l s ) i f o i j i r i t s o d s o l i e n e f r n p r m P o e ( a v e a u q ' t e n t n u / h r t h d i ' ' A ) : r ) s ; e } s $ d : ) e { ' ; t f p s i } a l ) t e ; h P P a a t r h a } m ` ; } ) ; Benefits of using Nitro Storage API:
Stable operation in build environments like Vercel Avoids OS-specific path separator issues Nitro optimizes file access based on the environment Why fs fallback is needed:
In development mode (npm run dev), serverAssets may not function fully, so direct fs reading is added as a fallback. The Storage API takes priority during builds.
Step 3: Wrap with a Composable# e } x ; p c } r c o o ; e o r n t } } t m t s r u p t y i } } c c r r o c f a o e n s o f { r e c i r t n t a n e ( e l o f e c s u { b s t i t s n t h o r l t c m D u e C S s ( u l n f e h p u r l e t ! r ( e e s u L o r n { i r r n e . n t / s o r i e v r e r e u c u e c t n a n e e s a r r l h s L a . g w t r s p w o r l L e o l m a p o a r o ; o L c D e S i s A o n i ) r c o a a t S t i P n s t ( a c l t a G d I s e { ` l a D a . / $ e e . r E D l a s S f : d o e r a D t = e S e o = k s r t a a r R t F e ) p o a t a v : c e s a o r a = s e h t n w r n } . y r U ( c ' a e s l ; t n ) s ` h t i t e o s c e / t u . a { a d e r j d = ( S p i x f n s i > f e i r i e o n i r / e s t n n g { l l c t c u ( : e e o t h l ) P r c l o ( l ; $ a a y n ` ; { t A l / f h P - f s d i : I d r t a l a o a t e s t m t a P t a i / a r / p c $ t i $ u { h n { b h f } g f l o i ` ) i i s l , l c t e = e i P e > P n a r a g t r { t h o h ( } r } S ` ) ` 3 ) ; ) , ; ; N e t l i f y , e t c . ) Why client-side fallback is important:
When deploying SSG-generated static sites to S3 or Netlify, Server API routes don’t exist. Therefore, during client-side navigation, JSON needs to be fetched directly from /data/.
Step 4: Use useAsyncData in Page Components# < c c } c < s o o ) o / t / c n n c r ; n s e < t r s A s o e A s c m d / e i t l t n t c t r p i { d m p w s u c i l v { i p t { a { t r e p p a v l y n s o t t p > a s f s d r s s > e - o t e e a e r t > f s e t t w t s e s s o t > u c r a u s a r . p h a : l u f = = t L p t l e " i l o p t l c p t a c w a = ; y o o l n a i g m s e g l t e a w p t = D h D w i u } " a a a t t i } t t u t i h e n s a s a t d " e c ( p > } A } f o ( o s e m ) s = y = t p t n c u = s u c a h t > " s D w L e e a a o d p : L t i c a k o a t a g e c l e y a u D D = l s a a " D e t t p a A a a o t s ( . s a y ' v t ( n j a . ) c s l i ; D o u d a n e " t a ? > a p . ( i d ' / a m n t y o a - d p e a / g p e o [ - s ] d t ) a s ; t . a j ' s , o n a ' s ) y ; n c = > { Important Points# 1. useAsyncData is Required# If you await without useAsyncData, it will also execute on the client side.
c c o o n n ❌ s ✅ s t t B C a d o { d a r t r d e a e a x c t a = t a m p a e } l w x e a a = i m t p a l w f e a e i t t c h u L s o e c A a s l y D n a c t D a a ( t ' a p ( o ' s k t e s y . ' j , s o n ' ) = ; > f e t c h L o c a l D a t a ( ' p o s t s . j s o n ' ) ) ; 2. Keys Must Be Unique# The first argument key of useAsyncData must be unique across pages/components.
c } o ) n r ; U s e s t t e u { r a n d u a a n t w i a a q i u } t e = f k e e a t y w c a h p i L e t o r c u a p s l a e D g A a e s t y a n ( c ` D p a o t s a t ( s ` / p $ o { s r t o - u $ t { e r . o p u a t r e a . m p s a . r i a d m } s . . j i s d o } n ` ` , ) ; a s y n c = > { 3. Access Safely with computed# Since the return value of useAsyncData is a Ref, wrapping it with computed is safe when using it in templates or logic.
c c c o o o n n n s ✅ s s t t t W { r t i a i t d p t e a l m t w e s a i : t = = h p c c a c o o g o m m e m p p D p u u a u t t t t e e a e d d d ( ( } ( ( ) ) = = = a > > w a p p i a a t g g e e u D D s a a e t t A a a s . . y v v n a a c l l D u u a e e t ? ? a . . ( t i . i t . t e . l m ) e s ; [ ] ) ) ; ; 4. Watch Out for Variable Scope# Variables defined inside the useAsyncData callback cannot be used outside the callback.
c } c c c } c o ) o o o ) o n c r ; n n n r ; n ❌ s o e s ✅ s s e s t n t t t t t t B s u C u a { t r i o t { r i d n t r y n t d t e r p d e e a y a m e e a a m x t p w s c s t w s a a e a t a a m s i = = i = p } t e } t l = [ x [ [ e = f ' a ' = f ' [ e x m a e x a ' t ' p ' a t ' w a c , l , w c , a ' h e a h i , D ' ' i D ' t a y b t a y ' t ' ' t ' u b a , ] u a , s ' ( ; s ( e ] ) e ) A ; ; A ; s . s . y t y t n y D n y c p e c p D D e f D e a e s i a s t f ] n t ] a i ; e a ; ( n d ( ' e ' k d o k e u e y i E t y O ' n r s ' K , s r i , i o d a d r e a s e : s y c y n c t a n c a y l c l p l l e b b s a a c = c i k = > k s > { n { o t d e f i n e d Result# Static sites generated with this method:
Data is embedded in HTML : Fast initial renderingData is saved in _payload.json : Used during client-side navigationNo requests to the original JSON files : Reduced network load[ P S p [ D → D a e u G a u g r b e t N r e v l n a o i ↓ e ↓ i e n C r c r i J g o u u / a s S m s A s d t O S p e P e a e e N S o A I S t d m G n s t a b l e y R o / H e o B n n o r * T d a u t c u a . M d d i D t g j L e i l a e e s ] d n d t o g ] a ( n i / N n n a i e p t _ e i r p d / o a e l y d o S l c t o o a o a n l r d - a . t d g j h a e s e t o a A n c / P l [ I i . ) e . n . t p a s t i h d ] e )
Alternative Approaches# For Small JSON Files: Direct Import# If the file size is small (under 50KB) and dynamic paths are not needed, direct import is the simplest approach.
[ P S p [ D → D a e u G a u g r b e t N r e v l n a o i ↓ e ↓ i e n C r c r i J g o u u / a s S m s A s d t O S p e P e a e e N S o A I S t d m G n s t a b l e y R o / H e o B n n o r * T d a u t c u a . M d d i D t g j L e i l a e e s ] d n d t o g ] a ( n i / N n n a i e p t _ e i r p d / o a e l y d o S l c t o o a o a n l r d - a . t d g j h a e s e t o a A n c / P l [ I i . ) e . n . t p a s t i h d ] e )
For Content Management: Nuxt Content# If you are building a site that handles markdown or JSON content, the Nuxt Content module is suitable.
[ P S p [ D → D a e u G a u g r b e t N r e v l n a o i ↓ e ↓ i e n C r c r i J g o u u / a s S m s A s d t O S p e P e a e e N S o A I S t d m G n s t a b l e y R o / H e o B n n o r * T d a u t c u a . M d d i D t g j L e i l a e e s ] d n d t o g ] a ( n i / N n n a i e p t _ e i r p d / o a e l y d o S l c t o o a o a n l r d - a . t d g j h a e s e t o a A n c / P l [ I i . ) e . n . t p a s t i h d ] e )
Summary# To combine SSG with local JSON in Nuxt 4:
Use the Nitro Storage API to load JSON in Server API routes Always wrap with useAsyncData Access data safely with computed Don’t forget to implement client-side fallback With this approach, all data is prefetched during build time, preventing additional API calls on the client side.
Reference Links#