<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ben Bachem</title><description>Hey there! I&apos;m Ben. I build all kinds of digital things.</description><link>https://benbachem.de/</link><language>en</language><item><title>Connected and presentational components</title><link>https://benbachem.de/blog/connected-components/</link><guid isPermaLink="true">https://benbachem.de/blog/connected-components/</guid><description>My favorite way of organizing React codebases</description><pubDate>Thu, 10 Sep 2026 06:40:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been meaning to write about this for ages. I believe for most applications splitting your React codebase into “Connected” and presentational components is most likely the single best architectural choice you can make. At Superchat we followed the pattern religiously. Now that I’m at Langfuse, code is not organized this way and it has made me a little uncomfortable with the codebase. I think it’s almost as strong as the feeling I remember having after using TypeScript for a while and then going back to an untyped JavaScript codebase, so I thought it was finally time to sit down and write about it.&lt;/p&gt;
&lt;h2 id=&quot;the-pattern&quot;&gt;The Pattern&lt;/h2&gt;
&lt;p&gt;What do I mean by “Connected” and presentational components? Quite simply, a connected component is allowed to interact with your API while a presentational component isn’t. If you think about components as functions, which they essentially are, this pattern means splitting your functions into pure functions and functions with side-effects. Well, not perfectly pure functions, since you’re not banning &lt;em&gt;all&lt;/em&gt; side-effects, only API calls.&lt;/p&gt;
&lt;p&gt;You might have read about this pattern before. Dan Abramov wrote about &lt;a href=&quot;https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0&quot;&gt;presentational and container components&lt;/a&gt; over a decade ago. What I’m arguing for here is really a subset of what he calls “Pure and Impure” components in his post, narrowed down to a single kind of impurity I care about: calling your API. Dan has since walked the broader pattern back, but I still believe this subset is one of the most useful ways of organizing a React codebase.&lt;/p&gt;
&lt;p&gt;To actually differentiate them, I like to simply prefix component names with “Connected”, and enforce via ESLint that components without the prefix can’t make API calls, more on that later. At its core, it’s similar to function coloring&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, from the name alone, you know whether a component makes API calls or not. An example component directory in a project of mine might look like the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;src/components/UserCard/ConnectedUserCard.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;src/components/UserCard/UserCard.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;src/components/UserCard/UserCard.stories.tsx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the &lt;code&gt;ConnectedUserCard&lt;/code&gt; being as simple as this:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; React &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; UserCard &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;./UserCard&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; { useUser } &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;@repo/react-api-client&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; UserCardProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ComponentProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; UserCard&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; ConnectedUserCardProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Omit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;UserCardProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; { id&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; ConnectedUserCard&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(props&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; ConnectedUserCardProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; userQuery&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; useUser&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;userQuery&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.data) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;UserCard&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; isLoading&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; userQuery&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;UserCard&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;      username&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.username}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;      bio&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.bio}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;      location&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.location}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;predictability&quot;&gt;Predictability&lt;/h2&gt;
&lt;p&gt;Now looking at the example above, you might be wondering why I’d bother introducing this split. Why do I voluntarily add this additional boilerplate of a wrapping component over mixing the API with the presentation? What do I actually gain from lifting one API calling hook from the &lt;code&gt;UserCard&lt;/code&gt; into the &lt;code&gt;ConnectedUserCard&lt;/code&gt; wrapper? Let’s look at the benefits.&lt;/p&gt;
&lt;p&gt;Let’s take the user card component from above and assume we don’t have a formalized split between connected and presentational components. Is the following a good idea?&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  [alice&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; bob&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; simon&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; john]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.map&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;((user) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;UserCard&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.id} &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{user} /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  ));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’d say if you are unsure whether the component does API requests, probably not. Let’s say the component fetches some metadata of the user and then renders the user’s name and a bio. This means the code above might now fire four API calls.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Now imagine dropping it into a table with hundreds of rows, oblivious to its network behavior.
If, instead, I know the component is not doing an API call just by looking at its name, I can compose it any way I want as long as I am able to satisfy the prop types. This makes my component very predictable and thus leads to increased reusability.&lt;/p&gt;
&lt;h2 id=&quot;testing-without-a-network&quot;&gt;Testing Without a Network&lt;/h2&gt;
&lt;p&gt;The next major benefit I see is that testing components that do network requests in isolation is a pain. Most likely it means setting up network mocks or spinning up a server that serves the real API. Maybe it also means you’re not going to bother with testing the component in isolation at all, instead you’re simply going to pop it in its slot in the real page.tsx, run the dev server, log in, and click around. I hope you’d at least do that.
But let’s face it, the feedback loop of clicking around in the real application is slow. Maybe it’s not even possible to get to a component you want to test because it’s buried in step five of the billing form that only appears when upgrading from an IP address detected to be in Japan. Now what? Are you going to rewrite the outer conditions just to be able to see the component once? What if you forget to put the right conditions back after testing? Maybe instead you could click through the flow using a proxy routed through Japan? Or change some rows in the database to place the system in the desired state?&lt;/p&gt;
&lt;p&gt;With a component that renders data received via props, I don’t need to do any of that. I just open it up in Storybook or place it on a temporary page and make sure to satisfy its prop types with a fixture. Compared to clicking around in the real application, this is easier, faster, and has the ability of being reliable when used in automated tests. That means people will actually do it. Not only that but coding agents will also have an easier time, as is often the case with tooling&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Admittedly, the approach above wouldn’t test the data fetching itself; you’d verify that separately. But being able to test most of your UI without a network is powerful. If you have a well-defined API and know the types of your responses, you can basically build out most of the UI of a newly planned feature simply by passing fixtures as props and then wiring up the components at the end through a shallow layer of connected components that contain simple API fetching hooks. In the end, all of this leads to increased reliability as UI is actually looked at and tests are actually written.&lt;/p&gt;
&lt;h2 id=&quot;preview-deployments&quot;&gt;Preview Deployments&lt;/h2&gt;
&lt;p&gt;As I mentioned above, my tool of choice for developing components in isolation is Storybook. It has the added benefit of being very simple and cost-effective to set up preview deployments as it can be compiled to static HTML, CSS, and JS which you can very cheaply serve. At Langfuse, we just push it to Vercel, and at Superchat, we uploaded it to S3 and pointed a CloudFront distribution at it. If I had to place preview deployment approaches in a quadrant of cost vs. usefulness, I think Storybook would generally come out as a no-brainer.&lt;/p&gt;
&lt;p&gt;Here’s my rundown of alternatives:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Full application preview&lt;/strong&gt;&lt;br&gt;
By this I mean full preview environments that include a real backend and database.
This is very powerful as in theory it allows you to test any kind of change, but is complicated to set up and can be expensive. It’s what we are currently doing at Langfuse.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client-only previews&lt;/strong&gt;&lt;br&gt;
By this I mean deploying the client-only, pointing it to a single staging or preview environment API.
It’s much easier to do and generally cheaper since you don’t need separate API server and database instances, but you lose the ability to make changes that span both client and API in the same PR. This is what we used to do at Superchat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Frontend with mocked backend&lt;/strong&gt;&lt;br&gt;
You can do this with tools such as &lt;a href=&quot;https://mswjs.io&quot;&gt;Mock Service Worker&lt;/a&gt;. I have not done this myself as I always had both Storybook with the pattern described in this post and some sort of preview environment with a real API available, but I’ve read about &lt;a href=&quot;https://oxide.computer&quot;&gt;Oxide&lt;/a&gt; doing this well for their &lt;a href=&quot;https://github.com/oxidecomputer/console&quot;&gt;console&lt;/a&gt;.
This can be done as a Storybook-only deployment that feels interactive, but you cannot test features end-to-end as the server is fake.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Storybook-only previews&lt;/strong&gt;&lt;br&gt;
By this I mean, no API at all, just components with fixtures as props living in Storybook.
I’d argue this is easiest and cheapest. Downside being it’s less interactive, you cannot test auth or API interactions etc. Especially with smaller changes that touch existing UI, a Storybook-only preview might be enough to verify a change is safe to merge.&lt;/p&gt;
&lt;p&gt;This is also not necessarily an either/or question. Both now at Langfuse and previously at Superchat, we simply do both, having Storybook previews in addition to a way of testing the application in a more end-to-end manner.&lt;/p&gt;
&lt;p&gt;Now the usefulness of Storybook scales with the amount of code it can cover and this is what the principle described here really gets to, sticking to it increases the share of the code that is easy to cover. I’d estimate at Superchat around 70% of the frontend code was not reliant on the API and could be tested in isolation, we could have probably pushed that to above 90% but sometimes took the slightly lazy route of prefixing a component with “Connected” and not bothering to split it up. This is another thing I like about this practice, it does not get in your way and there’s an out for when you have to deliver fast.&lt;/p&gt;
&lt;h2 id=&quot;loading-states&quot;&gt;Loading States&lt;/h2&gt;
&lt;p&gt;The last advantage I wanted to highlight is that following the pattern gives you a natural boundary for loading states. Every connected component can return a skeleton version of the wrapped component. Because the presentational component is right there and driven purely by props, rendering a placeholder that mirrors its shape is easy, so more often than not the default loading state ends up being a skeleton rather than a spinner. The skeleton tells the user roughly what’s coming and often can already match the final shape and dimensions so the page doesn’t jump around when the data arrives. Spinners are a lazy default, and connected components make this obvious.&lt;/p&gt;
&lt;h2 id=&quot;enforcing-the-pattern&quot;&gt;Enforcing the Pattern&lt;/h2&gt;
&lt;p&gt;Now you might have read up to here and thought to yourself: “This sounds all nice in theory but I could never teach my team to stick to this in practice.” Don’t worry, I don’t think coming up with principles like this without an automated way of enforcing them is any good either. To be honest, in the five years I was leading our frontend development at Superchat, I probably would have introduced my fair share of rule violations without being reminded of it and that was before the age of 10k LOC PRs from coding agents. Here’s how we enforced the pattern:&lt;/p&gt;
&lt;p&gt;We used ESLint with &lt;a href=&quot;https://github.com/javierbrea/eslint-plugin-boundaries&quot;&gt;eslint-plugin-boundaries&lt;/a&gt; which is a plugin meant to enforce architectural boundaries in your projects. It allows you to divide files into what it calls “elements” and then enforce the relationship of said elements. In the case of Superchat, we split components according to their filepath into connected and presentational components, then disallowed imports of our api-client in presentational components. We also disallowed importing the api-client from hooks. We then configured the boundaries plugin to disallow importing components whose names started with “Connected” in a component whose name didn’t, effectively enforcing our way of function coloring.&lt;/p&gt;
&lt;p&gt;To make this work, we exclusively used TanStack Query to make API calls. All the querying and mutation logic was abstracted into an internal &lt;code&gt;@superchat/api-client&lt;/code&gt; package within our monorepo. This meant there was a single entry point that we could check for.&lt;/p&gt;
&lt;p&gt;Here’s a sample ESLint configuration on how this could be done:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; boundaries &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;eslint-plugin-boundaries&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    plugins&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; { boundaries }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    settings&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;      // Only look at boundary types within `src`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;      &quot;boundaries/include&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;src/**/*.{js,jsx,ts,tsx}&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;      // Define the &quot;elements&quot; boundaries knows about.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;      &quot;boundaries/elements&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;hooks&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          mode&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;file&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          pattern&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;src/hooks/*.{js,ts}&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;connected-components&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          mode&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;file&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          pattern&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;src/components/**/Connected*.{jsx,tsx}&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;presentational-components&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          mode&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;file&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          pattern&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;src/components/**/*.{jsx,tsx}&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      ]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    rules&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;      // Keep the API client out of presentational components and out of hooks.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;      &quot;boundaries/external&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;        &quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          default&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;allow&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          rules&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;            {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;              from&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;presentational-components&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;hooks&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;              disallow&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;@repo/react-api-client&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;              message&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;                &quot;Do not import @repo/react-api-client here. Only connected components may talk to the API client directly.&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;            }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          ]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      ]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;      // Enforce the &quot;function coloring&quot; convention&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;      &quot;boundaries/element-types&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;        &quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          default&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;allow&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          rules&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;            {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;              from&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;presentational-components&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;              disallow&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;connected-components&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;              message&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;                &quot;Presentational components cannot import connected components. Pass data down as props instead.&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;            }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;          ]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      ]&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The one downside we saw was that we could not abstract complicated API logic into hooks, but we got around it by either doing the data fetching first and then passing the data to hooks, or using components with render props such as a &lt;code&gt;&amp;lt;UserPreferenceProvider&amp;gt;{(user) =&amp;gt; ...}&amp;lt;/UserPreferenceProvider&amp;gt;&lt;/code&gt; over a &lt;code&gt;useUserPreference()&lt;/code&gt;. We thought about introducing the connected / non-connected split also to hooks but never had a compelling enough argument to justify the additional complexity.&lt;/p&gt;
&lt;p&gt;Coming back to Dan’s original post from earlier, there are two places where I explicitly disagree with him. The first is his later update claiming the split is an implementation detail, that you should be able to replace a presentational component with a container without touching any call sites. I think that’s exactly backwards: in our case the value of the split lives at the call site, you can tell from the component’s name whether it will do network requests. Make it an implementation detail and you lose that. It’s also why I don’t follow his relaxed take on nesting, in my version a presentational component is never allowed to contain a connected one. The second is his view on logic: he originally argued to keep as much of it out of the presentational components, I’d argue the opposite, pack as much logic as you want into presentational components, all of it can then be easily tested in Storybook, just don’t put any API calls in.&lt;/p&gt;
&lt;h2 id=&quot;adopting-the-pattern&quot;&gt;Adopting the Pattern&lt;/h2&gt;
&lt;p&gt;If I were to go about adopting this in a greenfield project, I would actually be stricter than the version described so far. At Superchat, non-connected components were still allowed to access context, access session- or local storage or interact with the URL state, although the latter was discouraged and often discussed in code reviews. If I were starting from scratch I would consider restricting some of these too. Regarding URL state, for example, I’ve come to believe that components should ideally never read or modify URL state and the access should always be lifted to the framework boundary, e.g. a page. But I’ll make that argument another time.&lt;/p&gt;
&lt;p&gt;If you are not building a new application but are thinking about adopting this in your existing app, depending on the amount of existing code, this could be difficult. Normally, when adopting eslint rules you can grandfather existing violations by adding disable comments to them or using bulk suppressions but in this case this would not be a good idea, as it would not improve the predictability of the component. You still couldn’t tell if it was doing an API request from looking at the component name alone. If you want to adopt this pattern in an existing app, I’d probably do one of the following:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adopt it for a newly built domain and scope the ESLint configuration to its folder.&lt;/strong&gt;&lt;br&gt;
The downside here is that you’re not really getting the predictability of components or would at least always need to check where a component is imported from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reverse it and use a file extension prefix.&lt;/strong&gt;&lt;br&gt;
Instead of marking connected components, mark presentational ones and instead of using the component name, you could use their filepath, e.g. &lt;code&gt;UserCard.presentational.tsx&lt;/code&gt;. Then you could set up ESLint to enforce that presentational components cannot do API calls. This way you should be able to adopt this for newly written code only and keep the existing code you have untouched. Then over time, you could go through the application renaming files that already uphold the guarantees of presentational components and refactoring components that should but aren’t. The downside here is that you cannot tell from the component name alone, which reduces the amount of information you get when reading JSX as you’d always need to cross-reference it with the import section at the top of the file. I’d not use the component’s name for conveying the restrictions because this would lead to very ugly component names. Ideally you want most of your application’s components to be pure, say ~90%. When prefixing the 10%, this feels natural - a &lt;code&gt;Button&lt;/code&gt; keeps its name, same with a &lt;code&gt;UserCard&lt;/code&gt;. When you’re not prefixing the 10% connected components but the 90%, you’d also have to prefix components that obviously were not doing any network requests in the first place. Your &lt;code&gt;Button&lt;/code&gt; would become &lt;code&gt;PresentationalButton&lt;/code&gt;, your &lt;code&gt;Input&lt;/code&gt; would become &lt;code&gt;PresentationalInput&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Just rename every component and prefix it with “Connected”.&lt;/strong&gt;&lt;br&gt;
Over time, you could then remove the prefix from components that don’t need it. This would be a huge single change in git and I would not enjoy merging this beast of a PR, causing conflicts for everyone but if you’re a solo developer, this might be the most pragmatic way to adopt this if actually cleaning up the code according to the original rule is too much work for a single PR.&lt;/p&gt;
&lt;h2 id=&quot;where-the-pattern-breaks-down&quot;&gt;Where the Pattern Breaks Down&lt;/h2&gt;
&lt;p&gt;I’ll close this post by looking at types of applications where I think this pattern might be difficult to apply. At Superchat we struggled with it when we started building our &lt;a href=&quot;https://www.superchat.com/product/automations&quot;&gt;Automations&lt;/a&gt; feature, which is similar to Zapier or n8n. In Automations, we had a lot of complicated logic for rendering nodes in an interactive canvas and the API calls would generally only happen quite far down in the component tree. This meant that following the principle laid out here, most of the components had to be prefixed with “Connected” since using a connected component as a child of a non-connected component is not allowed. You could get around this a bit by using render functions in props but it can get unwieldy quickly and the effort of working around the pattern might outweigh the benefits. Generally, if you’d need most of the components in your application to be connected, the pattern might not be worth it. But let’s face it, many of us are not building these kinds of applications but rather traditional SaaS where most of the application is a collection of forms and tables. Even if you are building an application akin to Figma or Zapier, you might still have large parts of your application where this could be applied (think about all the settings pages). The nice thing about the pattern is that if splitting the components is too difficult, just prefix all of them with “Connected” and be done with it, you can always come back later and split components once you want to reuse them somewhere else.&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;I have used the term function coloring for a while, but never &lt;em&gt;really&lt;/em&gt; knew where it originated from. While writing this post, I went looking for a suitable reference and &lt;a href=&quot;https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/&quot;&gt;What Color is Your Function?&lt;/a&gt; came up as the source that apparently coined the term. The concept as I know it is about async functions and is generally considered a design flaw. I’m borrowing the term and applying it to components: a component’s color is determined by whether it makes network calls, and, like async’s color, decides where the component can be used. Instead of a flaw, I see the color as a feature and embrace the coloring rather than trying to design it away. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;I’m aware that if you are using GraphQL, queries might be merged and batched. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;If you’re interested, Steve Klabnik argued for a similar point in &lt;a href=&quot;https://steveklabnik.com/writing/the-most-important-thing-when-working-with-llms/#whats-good-for-the-goose-human-is-good-for-the-gander-llm&quot;&gt;The most important thing when working with LLMs&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>Switching from Backblaze to resticprofile</title><link>https://benbachem.de/blog/backblaze-to-resticprofile/</link><guid isPermaLink="true">https://benbachem.de/blog/backblaze-to-resticprofile/</guid><description>My experience switching backup providers</description><pubDate>Sat, 30 May 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have three Mac computers: one work laptop, one personal laptop, and one Mac mini that runs my homelab. The Mac mini has a 4-bay Terramaster D4-320 attached to it with some external storage and a large hard drive used for backing up the Mac and the external drives locally through Time Machine. I do not back up any data from the work or personal laptops as they don’t store anything that I couldn’t lose. All my code lives on GitHub and my personal repositories are being mirrored to my homelab. I have my photos and notes in iCloud, mirrored as well, and additional files such as an archive of RAW photos from my camera or old backups from my phones simply live on external drives connected to my Mac mini. So effectively, anything that is important to me is stored on the Mac mini.&lt;/p&gt;
&lt;p&gt;For the past few years, I was using Backblaze Personal Backup as an offsite backup. After recently reading that Backblaze stopped backing up the iCloud folder on macOS&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, I decided to take the time to switch to resticprofile backing up to a &lt;a href=&quot;https://www.hetzner.com/storage/storage-box/&quot;&gt;Hetzner Storage Box&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While the Backblaze Personal Backup price at $99 / year or $123 / year with 1-year version retention is very hard to beat, a Hetzner Storage Box is €156 / year for 5TB or €46 / year for 1TB if you have less data. For me, the slight increase in price was worth the additional control.&lt;/p&gt;
&lt;p&gt;I think it is worth noting, though, that unlike a cloud storage host like Dropbox or iCloud, or object storage like S3, data on a Hetzner Storage Box is not mirrored onto multiple servers. This means there is a higher chance of data loss. I would probably not advise using it to store a singular copy of critical data. Since I have two local copies of my data in addition to the data on the storage box, I think this is a very acceptable risk considering the cost savings over B2, S3, or Wasabi.&lt;/p&gt;
&lt;p&gt;Switching turned out to be easier than I expected and I’m genuinely happy with the outcome. I wanted to take the time to document the process so I have a resource that might help others in a similar position and to direct people to when talking about my own setup.&lt;/p&gt;
&lt;p&gt;The general setup of buying the Hetzner Storage Box and setting up resticprofile was very straightforward and something I’m going to skip over.&lt;/p&gt;
&lt;p&gt;I am using 1Password and am a big fan of their &lt;a href=&quot;https://www.1password.dev/ssh/agent&quot;&gt;SSH Agent&lt;/a&gt;, so I initially tried using that but noticed I couldn’t run restic unattended since the 1Password vault would need to be unlocked. Instead, I settled on just storing the SSH key and restic password on the filesystem.&lt;/p&gt;
&lt;p&gt;After setting everything up, my first complete restic run took a while. I didn’t have a comprehensive exclusions list in the first run, so restic was backing up a lot of irrelevant directories, of which many contained tiny files. Fortunately, restic handled interruptions well. I canceled it several times to tweak the config (the SSH connection also dropped on me a couple of times), and it always picked up right where it left of. One tweak I discovered was increasing the &lt;code&gt;restic_read_concurrency&lt;/code&gt; to 5, which seems to be configured by default to a value of only 2, you can probably push it even further.&lt;/p&gt;
&lt;p&gt;After the initial snapshot succeeded, I listed all the included files using &lt;code&gt;resticprofile ls latest&lt;/code&gt; and checked the list, updating my exclusions config. I then reran restic with only a few hours between runs a handful of times, comparing snapshots to further tweak my exclusion list. You can find my full config at the bottom of this post. Since it might be useful, a list of all snapshots can be shown by running &lt;code&gt;resticprofile snapshots&lt;/code&gt;, and two snapshots can be compared using &lt;code&gt;resticprofile diff [snapshot_id] [snapshot_id]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, after the initial setup, daily runs tend to back up only a few hundred megabytes and typically complete within less than five minutes, while the load on the computer is almost unnoticeable.&lt;/p&gt;
&lt;p&gt;The final thing I did was set up a Grafana dashboard to monitor everything. I had Grafana already set up anyway; otherwise, I would have likely skipped this.&lt;/p&gt;
&lt;p&gt;The entire process was pretty straightforward, and there are a few things I really like. The first being that running this setup makes me feel that I am in full control. Second, having the all the configuration be text-based instead of hidden in some GUI is great as I can check it into git. I had previously looked into setting up additional exclusions for Backblaze a few times but always put it off due to it being a little more complicated to configure. Lastly, also like that restic is encrypted by default. I saw that for a long time it was not even possible to run it without a password, and even now it’s &lt;a href=&quot;https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html#repositories-with-empty-password&quot;&gt;a little annoying&lt;/a&gt;, nudging people into secure defaults. Filippo Valsorda, who has done cryptography work at Google and Cloudflare, published a &lt;a href=&quot;https://words.filippo.io/restic-cryptography/&quot;&gt;detailed analysis of restic’s cryptographic design&lt;/a&gt; in 2017 and came away satisfied. That’s a meaningful endorsement, with the caveat of the post being almost ten years old.&lt;/p&gt;
&lt;p&gt;With everything said, there are a few small things that I think could be better. The Hetzner Storage Box web interface is very limited. It doesn’t even include a file browser; in order to see the contents, I need to connect via SSH or using an FTP client. This is not a big issue for me, since I only use the storage box for restic at the moment and I interact with it through the CLI anyway, but might be good to know.&lt;/p&gt;
&lt;p&gt;Another very minor annoyance I had was that while there is a &lt;a href=&quot;https://restic.readthedocs.io/en/latest/manual_rest.html#manual&quot;&gt;hosted manual for restic&lt;/a&gt;, the resticprofile docs only contain information about the existence of a help command without actually including its full output. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;For reference, here’s my final setup script:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Install resticprofile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; restic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; tap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; creativeprojects/tap&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; resticprofile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Symlink resticprofile config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ln&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -sfn&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;$SCRIPT_DIR/../resticprofile&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ~/.config/resticprofile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Create restic credential file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;op&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;op://Personal/Restic Password/password&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;$SCRIPT_DIR/../resticprofile/credentials&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Save restic SSH key to avoid 1password prompts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;op&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --out-file&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ~/.ssh/restic-key.pub&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;op://Personal/Hetzner Storage Box SSH Key/public key&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;op&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --out-file&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ~/.ssh/restic-key.pem&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;op://Personal/Hetzner Storage Box SSH Key/private key?ssh-format=openssh&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Associate key with host&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;PATTERN&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;*.your-storagebox.de&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;KEY&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;~/.ssh/restic-key.pem&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;^Match host[[:space:]]\+.$PATTERN&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ~/.ssh/config&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  cat&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; /tmp/ssh-match.tmp&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; &amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; EOF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt;Host $PATTERN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt;  IdentityFile $KEY&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt;  IdentitiesOnly yes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt;  IdentityAgent none&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;EOF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  cat&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; /tmp/ssh-match.tmp&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ~/.ssh/config&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; /tmp/ssh-config.new&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; mv&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; /tmp/ssh-config.new&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ~/.ssh/config&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; rm&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; /tmp/ssh-match.tmp&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ~/.ssh/config.new&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;Added Match block at top.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Setup resticprofile schedule&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Schedule can be manually triggered via &quot;/bin/launchctl start local.resticprofile.default.backup&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;resticprofile&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; schedule&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --all&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and here’s resticprofile configuration, with a few minor edits to allow publishing it online:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;#:schema https://creativeprojects.github.io/resticprofile/jsonschema/config-1.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;version &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;[global]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;prevent-sleep &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;[default]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;verbose &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;exclude-caches &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;priority &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;background&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;repository &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;password-file &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;${HOME}/.config/resticprofile/credentials&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;prometheus-push &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;http://localhost:9091/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;prometheus-push-job &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;resticprofile-backup&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;[default.backup]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;source &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;${HOME}&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;/Library&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;/Volumes/Disk 2&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;compression &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;max&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;read-concurrency &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;exclude &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  # Temporary &amp;amp; cache files&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.tmp&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.temp&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.swp&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.bzvol&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.DS_Store&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.Trash&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.Trashes&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.TemporaryItems&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.Spotlight-V100&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.DocumentRevisions-V100&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;**/tmp/&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  # MacOS default folders&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Applications&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Downloads&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/.Trash&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Caches/&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Developer/&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Updates/&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Metadata/&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/AppleMediaServices&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Application Support/com.apple.wallpaper&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Application Support/FileProvider&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Application Support/Animoji&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Application Support/CloudDocs&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Application Support/Knowledge&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Application Support/com.apple.ProtectedCloudStorage&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Application Support/com.apple.sharedfilelist&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Accessibility/com.apple.personalaudio.sqlite-wal&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Assistant&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Daemon Containers&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Group Containers&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Containers&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/HTTPStorages&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/HomeKit&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/IdentityServices&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/IntelligencePlatform&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/LanguageModeling&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/MediaAnalysis&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Safari&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Spelling&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Suggestions&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/Trial&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/WebKit&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/com.apple.AppleMediaServices&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/com.apple.bluetooth.services.cloud&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Library/com.apple.bluetoothuser&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Movies/TV/TV Library.tvlibrary&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Music/Music/Music Library.musiclibrary&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Pictures/Photos Library.photoslibrary/private/com.apple.mediaanalysisd/caches&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/Pictures/Photos Library.photoslibrary/resources/caches&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Apple&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Bluetooth&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Caches&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Documentation&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Image Capture&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Trial&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/User Template&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Keychains&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/LaunchDaemons&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Application Support/Apple&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Application Support/BTServer&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Application Support/ApplePushService&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Developer/CommandLineTools&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Developer/CoreSimulator&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Developer/PrivateFrameworks&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Application Support/com.apple.idleassetsd&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Application Support/iLifeMediaBrowser&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/WebServer/share/httpd&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Preferences/SystemConfiguration&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Preferences/OpenDirectory&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Preferences/com.apple.apsd.plist&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;/Library/Preferences/com.apple.wifi.known-networks.plist&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  # User level dotfiles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;${HOME}/.*&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  # Application specific folders&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  # ... excluded from the example&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  # Build artifacts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;node_modules&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.pnpm-store&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;__pycache__&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.pyc&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.pyo&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.venv&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;venv&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.tox&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.nox&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.rustup/toolchains&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.cargo/registry&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.cargo/git&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;.gradle&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;target&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;dist&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;build&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  # Large media / disk images&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.iso&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.img&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.hdd&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.qcow2&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.vmdk&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.vdi&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;  &quot;*.bzpkg&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Schedule can be manually triggered via &quot;/bin/launchctl start local.resticprofile.default.backup&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;*-*-* 10:00:00&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-permission &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;user&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-priority &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;background&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-log &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;${HOME}/.logs/resticprofile/logs/backup-{{ .Now.Format &quot;2006-01-02T15-04-05&quot; }}.log&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-lock-mode &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;default&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-lock-wait &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;15m00s&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;[default.retention]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;keep-last &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;keep-daily &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;keep-weekly &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;keep-monthly &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;keep-yearly &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;prune &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;after-backup &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;[default.check]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;read-data-subset &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;5%&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Once a week on monday at 12:00&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;Mon *-*-* 12:00:00&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-permission &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;user&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-priority &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;background&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-log &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;${HOME}/.logs/resticprofile/logs/check-{{ .Now.Format &quot;2006-01-02T15-04-05&quot; }}.log&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-lock-mode &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;default&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;schedule-lock-wait &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;15m00s&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://rareese.com/posts/backblaze/&quot;&gt;https://rareese.com/posts/backblaze/&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://creativeprojects.github.io/resticprofile/configuration/getting_started/index.html#inline-help&quot;&gt;https://creativeprojects.github.io/resticprofile/configuration/getting_started/index.html#inline-help&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>Don&apos;t pass className to your components</title><link>https://benbachem.de/blog/classname-and-style-props/</link><guid isPermaLink="true">https://benbachem.de/blog/classname-and-style-props/</guid><description>Why className and style props make your life harder, not easier</description><pubDate>Mon, 06 Apr 2026 11:22:00 GMT</pubDate><content:encoded>&lt;p&gt;I believe that not passing &lt;code&gt;className&lt;/code&gt; or &lt;code&gt;style&lt;/code&gt; props to components is an important principle for building scalable React applications. This post explains why, and what to do instead.&lt;/p&gt;
&lt;p&gt;It should be clear that what is written here &lt;em&gt;does not&lt;/em&gt; apply to headless components.&lt;/p&gt;
&lt;p&gt;The most apparent issue that can arise from passing these props is conflicting classes or properties.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  children&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  className&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  onClick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  children&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ReactNode&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  className&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  onClick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;      onClick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{onClick}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;      className&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;`bg-primary font-semibold text-center &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;className&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      {children}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;bg-green-500&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;Buy Now&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The button in this example will end up having the className &lt;code&gt;bg-primary font-semibold text-center bg-green-500&lt;/code&gt; in the DOM. What background color will the button in the above code sample take on?&lt;/p&gt;
&lt;p&gt;The answer depends on the order the classes appear in the compiled CSS, not the order they appear in the &lt;code&gt;className&lt;/code&gt; string. This is almost certainly not what you intended, and not something you can reason about reliably at a glance.&lt;/p&gt;
&lt;p&gt;Libraries like &lt;a href=&quot;https://github.com/dcastil/tailwind-merge&quot;&gt;tailwind-merge&lt;/a&gt; exist precisely to address this problem, but needing a workaround is a signal that the underlying design has a flaw.&lt;/p&gt;
&lt;p&gt;Now, assuming you run into the above issue, how do you know &lt;em&gt;how many&lt;/em&gt; conflicting classes you have across the rest of the codebase? How about the question: “Which background colors can our button component currently use?” - would you be able to answer that?&lt;/p&gt;
&lt;p&gt;This brings me to my next point: allowing a &lt;code&gt;className&lt;/code&gt; or &lt;code&gt;style&lt;/code&gt; prop to be passed makes it impossible to understand all states of a component without looking at every place it is used. Explicit props and TypeScript discriminated unions or enums make the full surface area of a component obvious at a glance.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; ButtonProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ComponentProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  variant&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;primary&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;secondary&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  size&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;small&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;medium&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;large&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  onClick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking at this type definition alone, you know the button has two variants, three sizes, and does exactly one thing on interaction. Compare that to a &lt;code&gt;className&lt;/code&gt; prop, which tells you nothing: any string is valid, and the only way to know what strings are actually in use is to search every usage of the component.&lt;/p&gt;
&lt;p&gt;The problem goes deeper than legibility, though. Programmatically reacting to the &lt;code&gt;className&lt;/code&gt; prop is infeasible too. This is the last issue I want to highlight.&lt;/p&gt;
&lt;p&gt;Say you have a &lt;code&gt;&amp;lt;SearchBar&amp;gt;&lt;/code&gt; component that shows match counts when navigating through search results in a long document. When enough space is available, you want to show “2 / 18 occurrences”. When space is tight, you want to shorten it to “2 / 18”.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;SearchBar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  query&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{query}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  onChange&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{setQuery}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{position}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  total&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{total}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  onMove&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{handleMove}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  className&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;max-w-96&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How should the component react to the &lt;code&gt;max-w-96&lt;/code&gt; class being passed? To the component, &lt;code&gt;max-w-96&lt;/code&gt; is just a string. Trying to parse it and then calculate what pixel size it might correspond to sounds far too complex a solution for such a simple problem.&lt;/p&gt;
&lt;p&gt;It would be easier to simply have a &lt;code&gt;size&lt;/code&gt; prop and set the max width inside the component.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; SearchBar&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  query&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  onChange&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  total&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  onMove&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  size&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; SearchBarProps&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{size &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;large&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;max-w-96&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;max-w-48&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{query} &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;onChange&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{(e) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; onChange&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;target&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.value)} /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        {position} / {size &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;large&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; `&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;total&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; occurrences`&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; total}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; onMove&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)}&amp;gt;Previous&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; onMove&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)}&amp;gt;Next&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;SearchBar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  query&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{query}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  onChange&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{setQuery}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{position}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  total&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{total}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  onMove&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{handleMove}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;/&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, CSS container queries can also solve the responsive part of this. But they are not ideal when the component needs to change its content based on available space, and they hide from the call site the fact that there is a meaningful difference between a “small” and a “large” search bar.&lt;/p&gt;
&lt;p&gt;I hope these three examples illustrate my reason for this principle. So, what should you do instead of passing &lt;code&gt;style&lt;/code&gt; or &lt;code&gt;className&lt;/code&gt; props?&lt;/p&gt;
&lt;h4 id=&quot;alternatives&quot;&gt;Alternatives&lt;/h4&gt;
&lt;p&gt;The alternatives I would reach for are the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Explicit variants.&lt;/strong&gt; Model the visual states of your component as named props, as shown in the &lt;code&gt;Button&lt;/code&gt; and &lt;code&gt;SearchBar&lt;/code&gt; examples above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full-width components with external constraints.&lt;/strong&gt; Make the component fill all available space, then control that space from the outside. Tools for constraining could be a wrapper &lt;code&gt;div&lt;/code&gt; with a max-width, a grid column, or a flex container. The component doesn’t need to know anything about its layout context; the parent handles that.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In all the years I’ve spent building React-based applications, these two alternatives have pretty much always worked for me. To be honest, I cannot recall a single non-headless component where I had to resort to passing a &lt;code&gt;className&lt;/code&gt; or &lt;code&gt;style&lt;/code&gt; prop instead.&lt;/p&gt;
&lt;p&gt;If this principle feels familiar, it should. At its core, it addresses the same problem that CSS Modules and scoped styles solve: preventing unintended style bleed by eliminating the global, cascading nature of CSS&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Passing &lt;code&gt;className&lt;/code&gt; into a component reintroduces that problem, just one layer up and through props instead of selectors.&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/seek-blog/the-end-of-global-css-90d2a4a06284&quot;&gt;“The End of Global CSS” by Mark Dalgleish&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>Prefer explicit discriminators over &quot;x&quot; in y in TypeScript</title><link>https://benbachem.de/blog/typescript-in-operator/</link><guid isPermaLink="true">https://benbachem.de/blog/typescript-in-operator/</guid><description>A look at why the &quot;x&quot; in y syntax in TypeScript can lead to dead code.</description><pubDate>Sat, 14 Feb 2026 11:07:00 GMT</pubDate><content:encoded>&lt;p&gt;In TypeScript, using the &lt;code&gt;if (&quot;x&quot; in y)&lt;/code&gt; syntax alters the definition of &lt;code&gt;y&lt;/code&gt;, since the compiler understands it as a guard statement, it will permit all subsequent property accesses of x. In practice this can lead to dead code. Often, a better approach is using explicit discriminator properties instead.&lt;/p&gt;
&lt;p&gt;Let me give you an example:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Strawberry&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  acidity&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Mango&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  acidity&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Strawberry&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Mango&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; printMetadata&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;acidity&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; fruit) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;    console&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.log&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.acidity);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;sweetness&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; fruit) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;    console&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.log&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.sweetness);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code snippet is valid and does not produce any compile-time issues, but according to the definition of the &lt;code&gt;Fruit&lt;/code&gt; type, it never has a “sweetness” property, so the &lt;code&gt;if(&quot;sweetness&quot; in fruit)&lt;/code&gt; condition doesn’t make much sense. TypeScript will not warn about the condition itself and it will also not complain about the subsequent use of the checked-for property. The compiler is doing what it’s told: since you checked for the property, so it assumes it might be there. This might seem logically incorrect but since TypeScript was meant to be incrementally adopted and type definitions used to often be incorrect, especially when many external libraries still used to be written in pure JavaScript with type definitions provided after the fact.&lt;/p&gt;
&lt;p&gt;In practice, this means using the &lt;code&gt;&quot;x&quot; in y&lt;/code&gt; syntax will often produce undetectable dead code. The most common scenario I’ve personally noticed is using the &lt;code&gt;&quot;x&quot; in y&lt;/code&gt; syntax to check for discriminated unions, then later deciding to remove one of the variants, forgetting to remove the condition.&lt;/p&gt;
&lt;p&gt;A better approach uses an explicit discriminator:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Strawberry&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;strawberry&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  acidity&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Mango&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;mango&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  acidity&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Strawberry&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Mango&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; printMetadata&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.type &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;strawberry&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.type &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;mango&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;    console&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.log&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;fruit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;.acidity);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, it’s impossible to construct an &lt;code&gt;if fruit.type ===&lt;/code&gt; condition that would allow you to access &lt;code&gt;fruit.sweetness&lt;/code&gt; and the compiler will complain anytime you try to.&lt;/p&gt;
&lt;p&gt;While the &lt;code&gt;in&lt;/code&gt; operator has its place, especially when dealing with truly unknown or untyped data from external sources, it should be avoided when working with well-defined union types. Using an explicit discriminator property in these cases leads to more robust and refactor-friendly code, letting the compiler do its job of catching errors for you.&lt;/p&gt;
</content:encoded></item><item><title>Getting Wezterm + Zellij to Open Files in VS Code</title><link>https://benbachem.de/blog/wezterm-open-files-in-vscode/</link><guid isPermaLink="true">https://benbachem.de/blog/wezterm-open-files-in-vscode/</guid><description>How to use WezTerm&apos;s hyperlink rules to open files in VS Code from Zellij</description><pubDate>Sat, 12 Apr 2025 19:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Edit from 2026-07-01: Zellij v0.44.0 now contains &lt;a href=&quot;https://zellij.dev/news/remote-sessions-windows-cli/#click-to-open-file-paths&quot;&gt;built-in functionality for opening links&lt;/a&gt;. I also maintain a &lt;a href=&quot;https://github.com/bezbac/zellij-link-extended&quot;&gt;fork of the builtin plugin with extended capabilities&lt;/a&gt;. I am no longer using the method described below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;For the past year and a half, I’ve been using WezTerm&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and Zellij&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; as my terminal and multiplexer of choice. Zellij has some hiccups here and there but the discoverability of shortcuts feels great, making it much easier to pick up than something like tmux in my opinion. Plus, both tools are written in Rust, which is always a bonus!&lt;/p&gt;
&lt;p&gt;Over the years, my terminal setup has evolved a bit. I started with iTerm2&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, which I liked for its features but found a bit slow when handling rapid screen updates (e.g., scrolling through large files in Nvim). I then switched to Alacritty&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; for its speed but eventually moved on because it lacked support for ligatures. Now, WezTerm + Zellij feels like the perfect balance for my workflow.&lt;/p&gt;
&lt;h2 id=&quot;the-missing-link&quot;&gt;The Missing Link&lt;/h2&gt;
&lt;p&gt;Despite being happy with my current setup, there was one feature I missed from iTerm2: &lt;strong&gt;the ability to open files in VS Code by simply clicking on their paths in the terminal.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;VS Code’s integrated terminal does this seamlessly, and it’s very handy when I want to quickly jump to a file mentioned in various contexts, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Failing test reports&lt;/li&gt;
&lt;li&gt;Linting warnings&lt;/li&gt;
&lt;li&gt;Compilation errors&lt;/li&gt;
&lt;li&gt;And more…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Selecting and copying file paths (or worse, typing them out manually) felt like a step backward in productivity. So I set out to get this functionality back in my WezTerm + Zellij setup.&lt;/p&gt;
&lt;p&gt;WezTerm supports custom &lt;em&gt;hyperlink_rules&lt;/em&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, which allow you to define how file paths are handled. However, the default implementation assumes you’re using WezTerm’s built-in tabs and panes so it can determine the current working directory. Unfortunately, this doesn’t work when using Zellij as the multiplexer.&lt;/p&gt;
&lt;p&gt;I couldn’t find a way to programmatically get the current working directory from a Zellij pane when clicking on a file path. So, I decided to take a more naive approach: &lt;strong&gt;search for the file in my development folder using &lt;em&gt;fd&lt;/em&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here’s the relevant part from my WezTerm config file:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Pull in the wezterm API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; wezterm &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;wezterm&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- This table will hold the configuration.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; config &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; find_absolute_file_path&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt;relative_path&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; search_dir &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; wezterm.home_dir &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;/Documents/Dev&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; success, stdout, stderr &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; wezterm.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;run_child_process&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/opt/homebrew/bin/fd&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;-p&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, relative_path, search_dir}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; success &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; first_line &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; stdout:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;match&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;[^\n]+&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; first_line &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; first_line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Update open uri function to open find:// links in vscode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;wezterm.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;    &quot;open-uri&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt;window&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt; pane&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt; uri&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; start, match_end &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; uri:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;find://&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; start &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; file_path &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; uri:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;sub&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(match_end &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; line_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; column_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; position_start &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; string.find&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(file_path, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; position_start &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;~=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                position &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; file_path:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;sub&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(position_start &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                file_path &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; file_path:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;sub&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, position_start &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; position_end &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; string.find&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(position, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; position_end &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;~=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                    column_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; position:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;sub&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(position_end &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                    line_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; position:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;sub&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, position_end &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                    line_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; position&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; absolute_file_path &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; find_absolute_file_path&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(file_path)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; absolute_file_path &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; vscode_url &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;vscode://file&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; absolute_file_path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; line_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;~=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; column_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;~=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                    vscode_url &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; vscode_url &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;:&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; line_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;:&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; column_number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                elseif&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; line_number &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;~=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                    vscode_url &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; vscode_url &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;:&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; line_number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;                wezterm.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;open_with&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(vscode_url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;                return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Use the defaults as a base&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;config.hyperlink_rules &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; wezterm.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;default_hyperlink_rules&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;table.insert&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    config.hyperlink_rules,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;        -- This regex can be tested at https://rustexp.lpil.uk/. Make sure to check `fancy-regex`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        regex &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;[/.A-Za-z0-9_-]+\\.[A-Za-z0-9]+(:\\d+)?(:\\d+)?&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        format &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;find://$0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- and finally, return the configuration to wezterm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; config&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;I define a custom URI scheme find:// that gets triggered by my hyperlink rule&lt;/li&gt;
&lt;li&gt;When a path is clicked, WezTerm captures it and passes it to my handler&lt;/li&gt;
&lt;li&gt;The handler extracts line and column numbers if present&lt;/li&gt;
&lt;li&gt;It then uses &lt;em&gt;fd&lt;/em&gt; to search for the file in my development directory&lt;/li&gt;
&lt;li&gt;If found, it constructs a VS Code URL with the proper format and opens it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The regex captures file paths that include extensions and optional line/column numbers (e.g., src/main.rs:10:5).&lt;/p&gt;
&lt;p&gt;There’s definitely room to improve this approach. The most obvious enhancement would be to narrow the search directory to the current project rather than searching my entire development folder. If anyone has insights on how to get the current working directory from a Zellij pane in WezTerm, I’d love to hear about it!&lt;/p&gt;
&lt;p&gt;For those interested in my complete setup, check out my dotfiles: &lt;a href=&quot;https://github.com/bezbac/dotfiles&quot;&gt;https://github.com/bezbac/dotfiles&lt;/a&gt;&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://wezterm.org&quot;&gt;https://wezterm.org&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://zellij.dev&quot;&gt;https://zellij.dev&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://iterm2.com&quot;&gt;https://iterm2.com&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://alacritty.org&quot;&gt;https://alacritty.org&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://wezterm.org/config/lua/config/hyperlink_rules.html&quot;&gt;https://wezterm.org/config/lua/config/hyperlink_rules.html&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sharkdp/fd&quot;&gt;https://github.com/sharkdp/fd&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>Don&apos;t use an app for that!</title><link>https://benbachem.de/blog/dont-use-apps/</link><guid isPermaLink="true">https://benbachem.de/blog/dont-use-apps/</guid><description>Of simple habits and digital artifacts that last</description><pubDate>Tue, 23 Apr 2024 06:14:00 GMT</pubDate><content:encoded>&lt;p&gt;Friends of mine know that I’m somewhat obsessive regarding tracking the media I consume. Over the past nearly ten years, I’ve written down almost every movie I’ve seen, every episode of every TV show I watched, and every book I’ve read, for some periods, even which pages on which dates. I keep track of all of this data in spreadsheets.&lt;/p&gt;
&lt;p&gt;I believe the reason I’ve been able to stick to the habit for this long is because &lt;em&gt;it’s easy&lt;/em&gt;. If I spend two hours watching a movie, spending 30 seconds to jot down some info in a spreadsheet row feels fine.&lt;/p&gt;
&lt;p&gt;Now, I could’ve used a service like &lt;a href=&quot;https://www.goodreads.com&quot;&gt;Goodreads&lt;/a&gt;, &lt;a href=&quot;http://al.librarything.com&quot;&gt;librarything&lt;/a&gt;, &lt;a href=&quot;https://letterboxd.com&quot;&gt;Letterboxd&lt;/a&gt;, &lt;a href=&quot;https://trakt.tv&quot;&gt;trakt.tv&lt;/a&gt;, &lt;a href=&quot;https://www.serializd.com&quot;&gt;serializd&lt;/a&gt;. There’s no shortage of apps that aim to do this for you.
However, companies get sold or go bankrupt, and open-source projects get abandoned. Sure, most of the time, this is not a big deal; you can export and migrate your data. Sure, it might be in a weird format, and you might need to wrangle it a little to make it fit, but that’s easy, right? Sure, it might be on short notice, and you’ll need to shift your priorities a little, but it’s no biggie, right?&lt;/p&gt;
&lt;p&gt;Do you really want to spend time migrating your data?
I’m confident I’ll still be able to open my CSV file in 10 years.&lt;/p&gt;
&lt;p&gt;When establishing a process, I try to think about the total cost of ownership. You wouldn’t buy the cheapest car when you already know it requires constant repairs. “If you want to create digital artifacts that last, they must be files you can control, in formats that are easy to retrieve and read. […] Apps are ephemeral, but your files have a chance to last.”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://stephango.com/file-over-app&quot;&gt;https://stephango.com/file-over-app&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item><item><title>My First Post</title><link>https://benbachem.de/blog/first-post/</link><guid isPermaLink="true">https://benbachem.de/blog/first-post/</guid><description>The try at introducing this website and why I built a blog in 2023</description><pubDate>Sat, 18 Mar 2023 12:33:00 GMT</pubDate><content:encoded>&lt;p&gt;After a long time of tweaking this site, I finally sat down to create this first blog post.
And, since I’ve wanted to have a blog since forever, I could never really put my finger on “why”, so I thought it might be a good idea to start with that as the topic of my first post.&lt;/p&gt;
&lt;p&gt;Like many others, I’ve been captivated by the personal websites of all the talented individuals on the web.
There are a lot of personal websites I look at in awe, sometimes I envy them for their seemingly perfect style&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, sometimes for the creativity behind the presentation&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; or, of course, for their content&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.
To me, these websites represent the best of the internet - a space for people to express themselves and share their interests.&lt;/p&gt;
&lt;p&gt;Growing up in a world where the internet was already abundant, I’ve always been drawn to the stories of creators, innovators, and entrepreneurs. I wanted to be part of that world, and now with me working in a startup feel closer to that goal than ever. But there was one thing missing – my little corner of the web.&lt;/p&gt;
&lt;p&gt;This blog should be my digital business card - my digital introduction - a place where I can proudly direct people when they ask about my work, my passions, and who I am as a person.&lt;/p&gt;
&lt;p&gt;Plus, I just really like building websites.&lt;/p&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://paco.me&quot;&gt;https://paco.me&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://petargyurov.com/bookshelf/&quot;&gt;https://petargyurov.com/bookshelf/&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://yoshuawuyts.com&quot;&gt;https://yoshuawuyts.com&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded></item></channel></rss>