<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: simonxabris</title>
    <description>The latest articles on Forem by simonxabris (@simonxabris).</description>
    <link>https://forem.com/simonxabris</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F141371%2F1813d925-8d3c-4cdf-9f15-1ab8d0957cd3.png</url>
      <title>Forem: simonxabris</title>
      <link>https://forem.com/simonxabris</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/simonxabris"/>
    <language>en</language>
    <item>
      <title>SSR Authentication Across Subdomains Using TanStack Start and Better-Auth</title>
      <dc:creator>simonxabris</dc:creator>
      <pubDate>Fri, 29 Aug 2025 08:21:41 +0000</pubDate>
      <link>https://forem.com/simonxabris/ssr-authentication-across-subdomains-using-tanstack-start-and-better-auth-21hg</link>
      <guid>https://forem.com/simonxabris/ssr-authentication-across-subdomains-using-tanstack-start-and-better-auth-21hg</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; When using TanStack Start with authentication handled on a different subdomain (e.g., api.domain.com), fetching the user session directly in your SSR loader won’t work as expected because server-to-server requests don’t include browser cookies. To fix the header flicker and SSR the correct user state, create a server function that forwards the original request’s cookies to your auth server. Call this function in your loader to ensure the authenticated user is available during SSR, eliminating the flicker and improving UX.&lt;/p&gt;




&lt;p&gt;The situation is the following, you're developing an app with Tanstack Start, you deploy it to &lt;code&gt;app.domain.com&lt;/code&gt;, but you have a separate API server that handles logic and authentication using &lt;code&gt;better-auth&lt;/code&gt;, which runs on &lt;code&gt;api.subdomain.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You create a header in your app that displays the logged in user, but  you see that when you log in and reload the app, the header flickers because initially it displays the logged out state and once the user data arrives, it flips to the logged in state.&lt;/p&gt;

&lt;p&gt;Of course you're a good developer who wants to deliver good UX, so you decide to fix it by fetching the user on the server and then the proper logged in/out state will get rendered into the HTML and the flicker is gone.&lt;/p&gt;

&lt;p&gt;What you do is you go and write a &lt;code&gt;loader&lt;/code&gt; function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createRootRouteWithContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QueryClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()({&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;authClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then in the &lt;code&gt;RootComponent&lt;/code&gt;, you have access to &lt;code&gt;user&lt;/code&gt; and can pass it to your &lt;code&gt;Header&lt;/code&gt; or &lt;code&gt;Navigation&lt;/code&gt; component, whatever you want to call it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRouteContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HeadContent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/head&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Navigation&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;min-h-screen bg-background&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Outlet&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TanStackRouterDevtools&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Toaster&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Scripts&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/AuthProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/body&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/html&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, it was very easy, now you go and open your app to marvel at your excellence and then boom, the flicker is still there. You scratch your head, you have no idea what's going wrong (I know I didn't).&lt;/p&gt;

&lt;p&gt;This is where if this article existed, it could have saved me quite a few agonizing hours, hope it can for someone else as well.&lt;/p&gt;

&lt;p&gt;The problem is that when &lt;code&gt;authClient.getSession&lt;/code&gt; makes a request to &lt;code&gt;api.domain.com&lt;/code&gt;, this request is going from your server that is running tanstack start to your server that is running auth, which means there are no cookies involved here, since cookies are stored in the browser. This is where you might say, but hey, the browser is making the request to the tanstack server to render the page, so that request must have the cookies, right? If you said this, you're absolutely right, this is what we're going to take advantage of to solve the problem.&lt;/p&gt;

&lt;p&gt;The solution is, you have to create a server function that fetches the user and then call that server function in your loader. I don't think this is documented anywhere (or maybe I haven't found it), but server function include all credentials like cookies from the original request the browser made, so you can write one like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerFn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getEvent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;authClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fetchOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cookie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// For brevity I copy all cookies here, but you might want to just parse out the auth cookie and send that.&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;call it in your loader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you're done. With this you can now get the authenticated user on the server and properly SSR your pages.&lt;/p&gt;

</description>
      <category>betterauth</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The design system struggle</title>
      <dc:creator>simonxabris</dc:creator>
      <pubDate>Tue, 14 Mar 2023 18:00:06 +0000</pubDate>
      <link>https://forem.com/simonxabris/the-design-system-struggle-eco</link>
      <guid>https://forem.com/simonxabris/the-design-system-struggle-eco</guid>
      <description>&lt;p&gt;Okay, so this post will basically be a rant about my experience working on a design system for almost 2 years now. I've failed to come up with a solution to this struggle, so I feel like I'll just write it out of myself and see if people out there are fighting the same thing and if they managed to solve it in any way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The struggle cycle
&lt;/h2&gt;

&lt;p&gt;The thing is I'm talking about is basically an endless cycle involving UX and engineering and it goes like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You work with a UX designer on the design system team and you come up with a design for a component, let's say an &lt;code&gt;Accordion&lt;/code&gt; where there's text on the left side and a chevron on the right, something like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Z4YZQK0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zcrmbd7gq5rtn3vcjmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Z4YZQK0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zcrmbd7gq5rtn3vcjmn.png" alt="An image that displays an accordion with text on its left side and a chevron icon on the right side" width="786" height="472"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You go into your cave and implement this design with whatever fancy and cool stack your team is using for your design system. You come up with the best API ever, you feel very proud, UX approves your implemented component and you release it, you go to sleep.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next day you wake up, have some breakfast, brew that perfect coffee and open slack and you see the following message: The &lt;code&gt;Accordion&lt;/code&gt; is not working, how can I place the chevron to be on the right side of the text? After you survived your heart attack, you start to wonder, why on earth would they want a chevron on the left side? That's not at all how the &lt;code&gt;Accordion&lt;/code&gt; is designed, so you ask the developer. The answer is that they got handed a design in let's say Figma, and they saw that there was an &lt;code&gt;Accordion&lt;/code&gt; with a chevron on the left side, so they imported your &lt;code&gt;Accordion&lt;/code&gt; component and searched for a way to place the chevron on the left side, but it was nowhere to be found. Of course it was nowhere to be found, since it wasn't a requirement, but now you're in a place where you are right because you implemented what the design was, the developer is right, because they just want to implement a design the were handed, so where did it all go wrong?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Understanding where it goes wrong.
&lt;/h2&gt;

&lt;p&gt;Well, here's where it all goes wrong. In design tools like Figma, it's just &lt;em&gt;too easy&lt;/em&gt; to customise a component. You pull the component in, you notice something you want to change, right-click, &lt;code&gt;Detach&lt;/code&gt; and boom, you're free to do whatever you want to the design. You shift that chevron to the left-side (which by the way might be a valid change), you hand it off to your developer and onto the next one. Without proper organisation wide education on how to use the design system and a &lt;em&gt;very&lt;/em&gt; disciplined approach from UX designers, this is bound to happen in every org. And by the time you, the developer on the design system team, catch this error, it's all too late. UX already spent a ton of time coming up with the design, they (understandably) feel attached to it, the developer on the product team also spent a bunch of time implementing that design and now you're faced with the following task.&lt;/p&gt;

&lt;h2&gt;
  
  
  The questions you have to deal with
&lt;/h2&gt;

&lt;p&gt;You have to tell them that the &lt;code&gt;Accordion&lt;/code&gt; is not supposed to be used like this, you have to involve your UX designer and you have to find the answer to a bunch of very tricky questions, usually in a short amount of time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this change make sense in the context of the system?&lt;/li&gt;
&lt;li&gt;Do you want to allow &lt;code&gt;Accordion&lt;/code&gt;s with chevrons on the left side company wide?&lt;/li&gt;
&lt;li&gt;Should you add some escape hatch so that this team can move forward with their feature and hack the chevron to the left side?&lt;/li&gt;
&lt;li&gt;If you add that, how do you make sure that it's not abused in other places and that your product(s) have a consistent UI?&lt;/li&gt;
&lt;li&gt;Do you tell them that they have to stop the feature work and redesign it and live with the chevron on the right side? Does your team even have the political power to say something like that?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a big can of warms that get opened repeatedly, ultimately, because UX tools are not good at enforcing the constraints of the system on designers. If you faced this problem in the past, please leave a comment, I'm very interested in how other organisations solve this problem.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>ux</category>
      <category>css</category>
    </item>
  </channel>
</rss>
