{"id":9365,"date":"2026-04-20T08:18:55","date_gmt":"2026-04-20T13:18:55","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=9365"},"modified":"2026-04-20T08:18:56","modified_gmt":"2026-04-20T13:18:56","slug":"building-a-blog-in-tanstack-part-2-of-2","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/building-a-blog-in-tanstack-part-2-of-2\/","title":{"rendered":"Building a Blog in TanStack (Part 2 of 2)"},"content":{"rendered":"\n<p>We left off this series with a functional blog in TanStack Start. We set up <a href=\"https:\/\/shiki.matsu.io\/\">Shiki<\/a> for code syntax highlighting and created server functions to inspect the file system, discover blog posts (in Markdown files), and build the final blog pages for all posts.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-blog-in-tanstack-part-1-of-2\/\">Building a Blog in TanStack (Part 1 of 2)<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-blog-in-tanstack-part-2-of-2\/\">Building a Blog in TanStack (Part 2 of 2)<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n\n\n\n<p>But we&#8217;ve still got work to do.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Performance Issues<\/h2>\n\n\n\n<p>It turns out that the Shiki setup, which lives in the top-level <code>async function getMarkdownIt()<\/code> method takes no small amount of time to set up. It&#8217;s not that the function is slow to call; it&#8217;s quite fast. But the initial parsing of this module is extremely slow. On my own modern MacBook Pro, it takes about 2 <em>seconds<\/em> (2000ms) to parse. This is because a lot of WASM is being loaded, which is what powers the parsing and formatting of\u00a0any\u00a0code we pass in.<\/p>\n\n\n\n<p>This means that when your web server first spins up and processes the import graph, this particular function will block the process for about two seconds. You might think, for a blog, this is an unimportant cost: it&#8217;s just spin-up time, after all, which happens only once.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cold Starts<\/h2>\n\n\n\n<p>Or does it? What if you deploy this site to Netlify, Vercel, or any other serverless platform, like AWS Lambda. With that runtime model, cloud functions will constantly be spinning up, to process requests. This spin-up time is called a &#8220;cold start,&#8221; and is a well-known issue with Serverless. Usually, cold start times are reasonable, and modern platforms like Netlify and Vercel will &#8220;pre-warm&#8221; serverless functions to minimize this cost from happening at all.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Going Static<\/h2>\n\n\n\n<p>Rather than debate the importance of minimizing cold starts for a blog that likely has few readers, let&#8217;s take a step back: do we even need a server? Blogs are inherently static. Any modern web framework provides a way to pre-render content statically: this\u00a0is exactly what we need. Why not just pre-render our blog pages so we can render them anywhere without any server processing? We could even just toss the built static assets onto a CDN.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pre-Rendering Pages<\/h2>\n\n\n\n<p>To start, let&#8217;s get into the <code>vite.config.ts<\/code> file and add a setting to the TanStack plugin:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\">tanstackStart({\n  prerender: {\n    enabled: <span class=\"hljs-literal\">true<\/span>,\n  },\n}),<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This enables pre-rendering. Now, during the build process, TanStack will crawl routes and the links within them. So it will start with our home <code>\/<\/code> route, build the page with all our blog posts, and then find each <code>&lt;Link><\/code> tag and crawl those. If those pages had links, they&#8217;d be crawled as well.<\/p>\n\n\n\n<p>When we run our build, we can see this in action during the build process:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"606\" height=\"566\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img5.png?resize=606%2C566&#038;ssl=1\" alt=\"\" class=\"wp-image-9379\" style=\"width:372px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img5.png?w=606&amp;ssl=1 606w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img5.png?resize=300%2C280&amp;ssl=1 300w\" sizes=\"auto, (max-width: 606px) 100vw, 606px\" \/><\/figure>\n<\/div>\n\n\n<p>We can look at the output of this build by peeking inside the\u00a0<code>.output<\/code>\u00a0folder, which is where the Nitro plugin (the default deployment adapter) creates our output:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"504\" height=\"972\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img6.png?resize=504%2C972&#038;ssl=1\" alt=\"\" class=\"wp-image-9380\" style=\"width:372px\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img6.png?w=504&amp;ssl=1 504w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img6.png?resize=156%2C300&amp;ssl=1 156w\" sizes=\"auto, (max-width: 504px) 100vw, 504px\" \/><\/figure>\n<\/div>\n\n\n<p>The <code>\/public<\/code> folder will contain everything that can be routed to directly. Our <code>index.html<\/code> is in there, as well as paths to both blogs, and the images from blog <code>post-2<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Running Content as a Static Website<\/h2>\n\n\n\n<p>Uploading these files to an S3 bucket would be a bit over the top for this post. To test true static rendering more simply, I&#8217;ve put together these two scripts on this post&#8217;s repo.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-string\">\"generate-static-site\"<\/span>: <span class=\"hljs-string\">\"npm run build &amp;&amp; rm -rf static-site &amp;&amp; mkdir -p static-site &amp;&amp; cp -r .output\/. static-site\"<\/span>,\n<span class=\"hljs-string\">\"start-static-server\"<\/span>: <span class=\"hljs-string\">\"npx tsx static-server.ts\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is a script to build and copy the contents to a folder called <code>static-site<\/code>. Then another to run <code>static-server.ts<\/code>, which looks like this, in its entirety.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">import<\/span> express <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"express\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> path <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"path\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { fileURLToPath } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"url\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> __filename = fileURLToPath(<span class=\"hljs-keyword\">import<\/span>.meta.url);\n<span class=\"hljs-keyword\">const<\/span> __dirname = path.dirname(__filename);\n\n<span class=\"hljs-keyword\">const<\/span> app = express();\n<span class=\"hljs-keyword\">const<\/span> PORT = <span class=\"hljs-number\">3003<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Serve static files from the static-site directory<\/span>\napp.use(express.static(path.join(__dirname, <span class=\"hljs-string\">\"static-site\/public\"<\/span>)));\n\napp.listen(PORT, <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Server is running on http:\/\/localhost:<span class=\"hljs-subst\">${PORT}<\/span>`<\/span>);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This script fires up an Express web server and points the static middleware at <code>\/public<\/code> inside the <code>static-site<\/code> folder we just copied our build output into.<\/p>\n\n\n\n<p>When we run this app, all of our pages work when we browse directly to them.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"><div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"990\" height=\"968\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img7.png?resize=990%2C968&#038;ssl=1\" alt=\"\" class=\"wp-image-9381\" style=\"aspect-ratio:1.0227337582041283;width:514px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img7.png?w=990&amp;ssl=1 990w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img7.png?resize=300%2C293&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img7.png?resize=768%2C751&amp;ssl=1 768w\" sizes=\"auto, (max-width: 990px) 100vw, 990px\" \/><figcaption class=\"wp-element-caption\">index page<\/figcaption><\/figure>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"><div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"453\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img8.png?resize=1024%2C453&#038;ssl=1\" alt=\"\" class=\"wp-image-9382\" style=\"aspect-ratio:2.2605331763518626;width:538px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img8.png?resize=1024%2C453&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img8.png?resize=300%2C133&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img8.png?resize=768%2C340&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img8.png?w=1446&amp;ssl=1 1446w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">blog post page<\/figcaption><\/figure>\n<\/div><\/div>\n<\/div>\n\n\n\n<p>These pages work if we navigate directly to them in our browser&#8217;s URL bar. But if we click around in our app, these pages fail.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"294\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img9.png?resize=1024%2C294&#038;ssl=1\" alt=\"\" class=\"wp-image-9383\" style=\"width:650px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img9.png?resize=1024%2C294&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img9.png?resize=300%2C86&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img9.png?resize=768%2C220&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img9.png?w=1248&amp;ssl=1 1248w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<p>Looking in the network tab makes this even clearer.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"119\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img10.png?resize=1024%2C119&#038;ssl=1\" alt=\"\" class=\"wp-image-9384\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img10-scaled.png?resize=1024%2C119&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img10-scaled.png?resize=300%2C35&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img10-scaled.png?resize=768%2C89&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img10-scaled.png?resize=1536%2C179&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img10-scaled.png?resize=2048%2C239&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<p>As we navigate, our server function is being called. Didn&#8217;t we pre-render these pages?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How TanStack Start Does Pre-Rendering<\/h2>\n\n\n\n<p>Our pre-rendered HTML file is indeed rendered by our Express server. But when it is, script tags containing the normal TanStack app will spin up and take over. At the end of the day, TanStack Start generates the same kind of application either way, except that, in this case, the initial render is served from a pre-generated HTML file rather than being server-rendered.<\/p>\n\n\n\n<p>From there on, <code>Link<\/code> tags trigger normal client-side loading, which triggers the server functions, as usual.<\/p>\n\n\n\n<p>TanStack Start does not try to morph itself into a full MPA framework just to handle static web apps. Instead, it gives you the primitives to achieve this yourself.<\/p>\n\n\n\n<p>We already saw the first, which was static pre-rendering. Now let&#8217;s look at the other.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Static Server Functions<\/h2>\n\n\n\n<p>Our client navigation will run either way, but the real problem is our server functions. To solve this, TanStack provides static middleware that we can apply to server functions. This causes our server functions, during the build, to record invocations and results, then save those payloads to simple JSON files in the build output.<\/p>\n\n\n\n<p>Let&#8217;s try it! Install it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">npm i @tanstack\/start-static-server-functions<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then import it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> { staticFunctionMiddleware } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@tanstack\/start-static-server-functions\"<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then apply it to our server functions:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> getPostContent = createServerFn()\n  .inputValidator(<span class=\"hljs-function\">(<span class=\"hljs-params\"><span class=\"hljs-params\">data<\/span>: { <span class=\"hljs-params\">slug<\/span>: <span class=\"hljs-params\">string<\/span> }<\/span>) =&gt;<\/span> data)\n  .middleware(&#91;staticFunctionMiddleware])\n  .handler(<span class=\"hljs-keyword\">async<\/span> ({ data }) =&gt; {<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, when we run our build, we see something new in there.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"862\" height=\"532\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img11.png?resize=862%2C532&#038;ssl=1\" alt=\"\" class=\"wp-image-9385\" style=\"aspect-ratio:1.6203019982519062;width:538px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img11.png?w=862&amp;ssl=1 862w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img11.png?resize=300%2C185&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img11.png?resize=768%2C474&amp;ssl=1 768w\" sizes=\"auto, (max-width: 862px) 100vw, 862px\" \/><\/figure>\n<\/div>\n\n\n<p>The <code>__tsr<\/code> folder naming refers to TanStack Router, and the <code>staticServerFnCache<\/code> contains the 3 server function calls from parsing our blog: one for the index page, and one each for the two posts we have.<\/p>\n\n\n\n<p>The plugin recorded those invocations and results, and more importantly, replaced those call sites with fetches to the JSON files in the <code>__tsr<\/code> folder.<\/p>\n\n\n\n<p>If we run our blog again, we can navigate and see much simpler fetches to those JSON files.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"137\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img12.png?resize=1024%2C137&#038;ssl=1\" alt=\"\" class=\"wp-image-9386\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img12.png?resize=1024%2C137&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img12.png?resize=300%2C40&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img12.png?resize=768%2C103&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img12.png?resize=1536%2C206&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/img12.png?resize=2048%2C275&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Concluding Thoughts<\/h2>\n\n\n\n<p>TanStack Start is a superb framework. It&#8217;s better known for features like strong static typing and flexible data loading. But as we saw here, it also offers creative ways to support static generation.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-blog-in-tanstack-part-1-of-2\/\">Building a Blog in TanStack (Part 1 of 2)<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-blog-in-tanstack-part-2-of-2\/\">Building a Blog in TanStack (Part 2 of 2)<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>A blog is a perfect use case for pre-rendering, so that the static build files can render all on their own. TanStack Start can even help with the server functions via middleware.<\/p>\n","protected":false},"author":21,"featured_media":9345,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[208,3,63,240],"class_list":["post-9365","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-blogging","tag-javascript","tag-ssr","tag-tanstack"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/tanstack-blog-1.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9365","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=9365"}],"version-history":[{"count":16,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9365\/revisions"}],"predecessor-version":[{"id":9423,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9365\/revisions\/9423"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/9345"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=9365"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=9365"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=9365"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}