We can easily create Client rendered pages (CSR) and Static generated pages (SG) using Next.js. But, often times, you’ll want your page to use a combination of those different rendering methods. Like, you want your static page to be generated with some data from api, then once the users browser has hydrated the static page, you want CSR to kick in. There is one way we can use this, by using the excellent SWR
plugin from Vercel (`creators of Next.js). Let’s have a quick look about it in this article.
First, let’s create a next app by running
1 2 | npx create-next-app blog-swr-demo -y |
Now we need to create a mock api to serve us articles/posts.
Let’s create a new file in pages/posts.js
and paste in
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span> posts<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span>title<span class="token operator">:</span> <span class="token string">"A"</span><span class="token punctuation">,</span> content<span class="token operator">:</span> <span class="token string">"asdf"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>title<span class="token operator">:</span> <span class="token string">"B"</span><span class="token punctuation">,</span> content<span class="token operator">:</span> <span class="token string">"qwert"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>title<span class="token operator">:</span> <span class="token string">"C"</span><span class="token punctuation">,</span> content<span class="token operator">:</span> <span class="token string">"zxcv"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>title<span class="token operator">:</span> <span class="token string">"D"</span><span class="token punctuation">,</span> content<span class="token operator">:</span> <span class="token string">"mnopq"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>title<span class="token operator">:</span> <span class="token string">"E"</span><span class="token punctuation">,</span> content<span class="token operator">:</span> <span class="token string">"aeiou"</span><span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Now, we’ll install the SWR
plugin
1 2 | npm i swr |
And create our blog component and also import the useSWR
hook from the plugin we just installed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">import</span> useSWR <span class="token keyword">from</span> <span class="token string">"swr"</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Blog</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div<span class="token operator">></span> Demo blog <span class="token punctuation">{</span> posts<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator"><</span>h4<span class="token operator">></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span>title<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h4<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span>content<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Now, let’s render the blog posts using the useSWR
hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token keyword">import</span> useSWR <span class="token keyword">from</span> <span class="token string">"swr"</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">fetcherFunc</span><span class="token punctuation">(</span><span class="token parameter">url</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Blog</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token string">"http://localhost:3000/api/posts"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span>data<span class="token punctuation">,</span> error<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useSWR</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> fetcherFunc<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>data<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator"><</span>div<span class="token operator">></span>fetching<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token keyword">const</span> <span class="token punctuation">{</span>posts<span class="token punctuation">}</span> <span class="token operator">=</span> data<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div<span class="token operator">></span> Demo blog <span class="token punctuation">{</span> posts<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token operator"><</span>h4<span class="token operator">></span>Article<span class="token operator">:</span> <span class="token punctuation">{</span>post<span class="token punctuation">.</span>title<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h4<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span>content<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>br<span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Now let’s create the static generated file for our page, for this we’ll modify our code like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Blog</span><span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// <<<<<<< changed here</span> <span class="token comment">// const url = "http://localhost:3000/api/posts";</span> <span class="token comment">// const {data, error} = useSWR(url, fetcherFunc);</span> <span class="token comment">// if (!data) return <div>fetching</div></span> <span class="token keyword">const</span> <span class="token punctuation">{</span>posts<span class="token punctuation">}</span> <span class="token operator">=</span> props<span class="token punctuation">;</span> <span class="token comment">// <<<<<<<< Also here</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div<span class="token operator">></span> Demo blog <span class="token punctuation">{</span> posts<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token operator"><</span>h4<span class="token operator">></span>Article<span class="token operator">:</span> <span class="token punctuation">{</span>post<span class="token punctuation">.</span>title<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h4<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span>content<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>br<span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getStaticProps</span><span class="token punctuation">(</span><span class="token parameter">context</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"http://localhost:3000/api/posts"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> posts <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token punctuation">(</span><span class="token keyword">await</span> res<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> props<span class="token operator">:</span> <span class="token punctuation">{</span> posts <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Let’s generate the page. For that run
1 2 | npm run build |
You’ll see this after it’s successfully built
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | > <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> build > next build info - Creating an optimized production build info - Compiled successfully info - Collecting page data info - Generating static pages (4/4) info - Finalizing page optimization Page Size First Load JS ┌ ○ / 3.46 kB 66.9 kB ├ └ css/c50ddf22b716c7b6b76d.css 660 B ├ /_app 0 B 63.4 kB ├ ○ /404 3.46 kB 66.9 kB ├ λ /api/hello 0 B 63.4 kB ├ λ /api/posts 0 B 63.4 kB └ ● /blog 1.96 kB 65.4 kB + First Load JS shared by all 63.4 kB ├ chunks/e82d01500e11e0131e78851aa17fd9f5e63d6c88.ebefd9.js 2.47 kB ├ chunks/f6078781a05fe1bcb0902d23dbbb2662c8d200b3.519eb7.js 11.3 kB ├ chunks/framework.e2fe4a.js 41.8 kB ├ chunks/main.1a4cca.js 6.67 kB ├ chunks/pages/_app.6b4817.js 531 B ├ chunks/webpack.50bee0.js 751 B └ css/6e9ef204d6fd7ac61493.css 194 B λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps) ○ (Static) automatically rendered as static HTML (uses no initial props) ● (SSG) automatically generated as static HTML + JSON (uses getStaticProps) (ISR) incremental static regeneration (uses revalidate in getStaticProps) |
Note, the if we keep adding posts to our mock api while our blog is running, the data doesn’t get refreshed. We need to reload the page to do that
Let’s fix that now. For that, we will pass the revalidateOnMount
option in our useSWR
hook
So, the final code will be
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <span class="token keyword">import</span> useSWR <span class="token keyword">from</span> <span class="token string">"swr"</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">fetcherFunc</span><span class="token punctuation">(</span><span class="token parameter">url</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Blog</span><span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token string">"http://localhost:3000/api/posts"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span>data<span class="token punctuation">,</span> error<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useSWR</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> fetcherFunc<span class="token punctuation">,</span> <span class="token punctuation">{</span>initialData<span class="token operator">:</span> props<span class="token punctuation">,</span> revalidateOnMount<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span>posts<span class="token punctuation">}</span> <span class="token operator">=</span> data<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>posts<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator"><</span>div<span class="token operator">></span>fetching<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div<span class="token operator">></span> Demo blog <span class="token punctuation">{</span> posts<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token operator"><</span>h4<span class="token operator">></span>Article<span class="token operator">:</span> <span class="token punctuation">{</span>post<span class="token punctuation">.</span>title<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h4<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span>content<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>br<span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getStaticProps</span><span class="token punctuation">(</span><span class="token parameter">context</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"http://localhost:3000/api/posts"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span>posts<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token punctuation">(</span><span class="token keyword">await</span> res<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> props<span class="token operator">:</span> <span class="token punctuation">{</span> posts <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Let’s check
Source code
https://github.com/Salekin-1169/blog-swr-demo
Learning Material