<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Thiyagu Arunachalam</title>
    <description>The latest articles on Forem by Thiyagu Arunachalam (@thiyagu_arunachalam_e36b8).</description>
    <link>https://forem.com/thiyagu_arunachalam_e36b8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1877591%2F93bbde9b-98d5-43c3-9124-ce04c95b5b91.jpg</url>
      <title>Forem: Thiyagu Arunachalam</title>
      <link>https://forem.com/thiyagu_arunachalam_e36b8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thiyagu_arunachalam_e36b8"/>
    <language>en</language>
    <item>
      <title>Day Zero of My Open-source project</title>
      <dc:creator>Thiyagu Arunachalam</dc:creator>
      <pubDate>Sat, 04 Apr 2026 16:24:11 +0000</pubDate>
      <link>https://forem.com/thiyagu_arunachalam_e36b8/i-built-a-stem-tools-site-2-years-ago-now-im-rebuilding-it-the-right-way-in-public-3fkd</link>
      <guid>https://forem.com/thiyagu_arunachalam_e36b8/i-built-a-stem-tools-site-2-years-ago-now-im-rebuilding-it-the-right-way-in-public-3fkd</guid>
      <description>&lt;h1&gt;
  
  
  Why I Rebuilt Kitsune Chaos from Scratch
&lt;/h1&gt;

&lt;p&gt;Every interactive learning tool I've ever loved eventually dies. The Flash ones went first. The ones built on unmaintained jQuery plugins followed. I've rebuilt this project three times now, and each time I've gotten a little smarter about what actually matters.&lt;/p&gt;

&lt;p&gt;This is the story of the third rewrite — and why I'm reasonably confident it's the last one for a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was wrong with the old version
&lt;/h2&gt;

&lt;p&gt;The original Kitsune Chaos was a single HTML page with inline scripts. It worked, barely. The pendulum simulator was a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element with 200 lines of procedural JavaScript living in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. The Ohm's Law calculator was a form with &lt;code&gt;oninput&lt;/code&gt; handlers.&lt;/p&gt;

&lt;p&gt;Shipping a new tool meant copy-pasting the whole page and editing it. There was no shared code. Every tool reimplemented the same slider, the same layout, the same color scheme — but slightly differently each time because I'd changed my mind between shipping them.&lt;/p&gt;

&lt;p&gt;The real breaking point: I wanted to add a blog. And I had absolutely nowhere to put it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new stack
&lt;/h2&gt;

&lt;p&gt;The new version is a Turborepo monorepo with four packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@kitsunechaos/physics&lt;/code&gt; — pure TypeScript physics helpers (no React, no DOM)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@kitsunechaos/ui&lt;/code&gt; — shared React components: &lt;code&gt;Slider&lt;/code&gt;, &lt;code&gt;Panel&lt;/code&gt;, &lt;code&gt;ToolShell&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@kitsunechaos/tools&lt;/code&gt; — the actual interactive tools, built with React + Vite&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@kitsunechaos/config&lt;/code&gt; — shared TypeScript and Tailwind configs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;apps/web&lt;/code&gt; Next.js app shells everything. It handles routing, SEO, and this blog. The tool components themselves have zero Next.js dependency — they're pure React and could theoretically be embedded anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why separate the physics math from React?
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;V = I × R&lt;/code&gt; doesn't need hooks.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@kitsunechaos/physics&lt;/code&gt; is plain TypeScript functions. You can unit test them without jsdom. You can import them in a Node script or a Cloudflare Worker. When I eventually add a signal path calculator or a projectile motion tool, the math gets added here first, and the React layer just calls it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool page pattern
&lt;/h2&gt;

&lt;p&gt;Each tool page in Next.js is roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OhmsLaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@kitsunechaos/tools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OhmsLaw&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ToolSkeleton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server-rendered: the page title, description, and category (for Google). Client-rendered: the actual interactive component.&lt;/p&gt;

&lt;p&gt;This means search engines see real content, and users don't get a blank screen while the JavaScript loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The two tools live right now — Ohm's Law and the Pendulum Simulator — are just proof that the architecture works. The interesting ones are coming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RLC circuit analyzer — impedance, phase angle, resonant frequency&lt;/li&gt;
&lt;li&gt;Projectile motion — launch angle, drag, range optimization&lt;/li&gt;
&lt;li&gt;Wave interference — superposition, standing waves, beats&lt;/li&gt;
&lt;li&gt;Lens equation — thin lens, focal length, magnification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm building these in public. The source is on GitHub. If you find a bug or want to suggest a tool, open an issue.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>turborepo</category>
      <category>nextjs</category>
      <category>ai</category>
    </item>
    <item>
      <title>Workers with AI Skills Earn 56% More. Here's the 5-Step Plan to Become One of Them</title>
      <dc:creator>Thiyagu Arunachalam</dc:creator>
      <pubDate>Thu, 02 Apr 2026 13:59:19 +0000</pubDate>
      <link>https://forem.com/thiyagu_arunachalam_e36b8/workers-with-ai-skills-earn-56-more-heres-the-5-step-plan-to-become-one-of-them-51dk</link>
      <guid>https://forem.com/thiyagu_arunachalam_e36b8/workers-with-ai-skills-earn-56-more-heres-the-5-step-plan-to-become-one-of-them-51dk</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;The pay gap between AI-fluent and AI-ignorant professionals is widening fast. Here's exactly what to do about it.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's not sugarcoat it: 2026 is a brutal year to be standing still in your career.&lt;/p&gt;

&lt;p&gt;While you're reading this, your company is quietly sorting its workforce into two buckets — those who can work with AI, and those who are being replaced by it. The uncomfortable truth is that most people don't realize which bucket they're in.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;56%&lt;/strong&gt;&lt;br&gt;
That's how much more workers with advanced AI skills earn compared to peers in the same role, according to PwC's analysis. Not a different job. The same job.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This isn't a future problem. It's happening now. And the window to act is smaller than you think.&lt;/p&gt;

&lt;p&gt;Here's the exact 5-step plan I'd follow if I had to future-proof my career starting today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Run a "Replacement Audit" on Your Own Job
&lt;/h2&gt;

&lt;p&gt;Open a blank document. List every task you did last week. Then, for each task, ask one question: &lt;em&gt;Could a well-prompted AI do 80% of this?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the answer is yes, that task is at risk. If no — that's your moat.&lt;/p&gt;

&lt;p&gt;Most people skip this step because it's uncomfortable. Don't. You can't protect what you haven't identified.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Pick ONE AI Tool and Go Deep — Not Wide
&lt;/h2&gt;

&lt;p&gt;The biggest mistake professionals make is dabbling. They try five AI tools, use each one twice, and then tell themselves they "know AI." That's not fluency — that's tourism.&lt;/p&gt;

&lt;p&gt;Pick the tool most relevant to your core work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude&lt;/strong&gt; — writing and analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; — code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Midjourney&lt;/strong&gt; — design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NotebookLM&lt;/strong&gt; — research&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spend 30 minutes a day for two weeks going deep. Learn its edge cases. Break it. Learn its limits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The professionals thriving in 2026 aren't avoiding AI — they're the ones actively learning to work alongside it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Make Your AI Wins Visible
&lt;/h2&gt;

&lt;p&gt;Skill without visibility is a hobby. When AI helps you cut a 4-hour task down to 45 minutes — document it. Tell your manager. Quantify it.&lt;/p&gt;

&lt;p&gt;The professionals getting promoted right now aren't necessarily the most technically skilled. They're the ones making the business case for their own value.&lt;/p&gt;

&lt;p&gt;Keep a simple &lt;strong&gt;"AI impact log"&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Old Time&lt;/th&gt;
&lt;th&gt;New Time&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After 30 days, that document becomes your performance review.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Rebuild Your "Human Edge" Stack
&lt;/h2&gt;

&lt;p&gt;Here's what AI still can't do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build real relationships&lt;/li&gt;
&lt;li&gt;Read a room&lt;/li&gt;
&lt;li&gt;Carry institutional memory&lt;/li&gt;
&lt;li&gt;Make judgment calls with incomplete information&lt;/li&gt;
&lt;li&gt;Communicate a nuanced idea to a skeptical audience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The World Economic Forum calls &lt;strong&gt;creative thinking, resilience, and leadership&lt;/strong&gt; the fastest-rising skills of 2026 — precisely because they complement what AI does.&lt;/p&gt;

&lt;p&gt;Invest in these deliberately. Take on a cross-functional project. Volunteer to present. Find the hard conversation at work that nobody else wants to have — and have it well.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Stop Waiting for Your Company to Upskill You
&lt;/h2&gt;

&lt;p&gt;Only &lt;strong&gt;54% of employees&lt;/strong&gt; used AI at work at all last year, despite most companies claiming AI is a priority. The upskilling gap is real — and it won't close itself.&lt;/p&gt;

&lt;p&gt;The people who win in this market are taking personal ownership of their development, not waiting for a training program that may never come.&lt;/p&gt;

&lt;p&gt;Spend &lt;strong&gt;one hour per week&lt;/strong&gt; — just one hour — learning something AI-adjacent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A YouTube tutorial&lt;/li&gt;
&lt;li&gt;A prompt engineering guide&lt;/li&gt;
&lt;li&gt;A conversation with someone who already does it well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compound it over 12 months, and you will be unrecognizable from where you started.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;The 56% wage gap isn't going to shrink — it's going to grow. The only question is which side of it you're on.&lt;/p&gt;

&lt;p&gt;The good news: you don't need to become an engineer. You don't need to take a six-month bootcamp. You need to &lt;strong&gt;start this week&lt;/strong&gt;, stay consistent, and make your progress visible. That's it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The window is open. Use it.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Stop Copy-Pasting That "Click Outside" Snippet — Use This Hook Instead</title>
      <dc:creator>Thiyagu Arunachalam</dc:creator>
      <pubDate>Thu, 02 Apr 2026 13:51:23 +0000</pubDate>
      <link>https://forem.com/thiyagu_arunachalam_e36b8/stop-copy-pasting-that-click-outside-snippet-use-this-hook-instead-nbj</link>
      <guid>https://forem.com/thiyagu_arunachalam_e36b8/stop-copy-pasting-that-click-outside-snippet-use-this-hook-instead-nbj</guid>
      <description>&lt;p&gt;Every React developer has written this code at least once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousedown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousedown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;You paste it from Stack Overflow. It works. You move on. Then you write it again next week for the modal. Then again, for the dropdown. Then again, for the tooltip.&lt;/p&gt;

&lt;p&gt;Press enter or click to view image in full size&lt;/p&gt;

&lt;p&gt;Photo by Lautaro Andreani on Unsplash&lt;br&gt;
Sound familiar?&lt;/p&gt;

&lt;p&gt;That’s exactly why I built @kitsunechaos/use-outside-click.&lt;/p&gt;

&lt;p&gt;The Problem&lt;br&gt;
Detecting a click outside an element is one of the most common UI patterns in React:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Closing a dropdown when you click elsewhere&lt;/li&gt;
&lt;li&gt;Dismissing a modal on backdrop click&lt;/li&gt;
&lt;li&gt;Collapsing a popover, tooltip, or color picker&lt;/li&gt;
&lt;li&gt;Hiding a mobile navigation drawer&lt;/li&gt;
&lt;li&gt;Yet every time, we write the same boilerplate — or worse, reach for a 6KB library that wraps it in a class component HOC from 2018.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Introducing @kitsunechaos/use-outside-click&lt;br&gt;
A dead-simple React hook that detects clicks outside any element. That’s it. No fluff.&lt;/p&gt;

&lt;p&gt;npm install &lt;strong&gt;&lt;em&gt;@kitsunechaos/use-outside-click&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Usage&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useOutsideClick&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@kitsunechaos/use-outside-click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Dropdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;useOutsideClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Open&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* dropdown items */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One import. One ref. One callback. Done.&lt;/p&gt;

&lt;p&gt;What Makes It Different&lt;br&gt;
✅ &lt;strong&gt;Zero Dependencies&lt;/strong&gt;&lt;br&gt;
No bloat. No peer dependency warnings. Nothing extra in your node_modules.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Fully Typed&lt;/strong&gt;&lt;br&gt;
Written in TypeScript from the ground up. Full type inference — no @types/ package needed.&lt;/p&gt;

&lt;p&gt;useOutsideClick(ref: RefObject, callback: () =&amp;gt; void): void&lt;br&gt;
✅ &lt;strong&gt;SSR Safe&lt;/strong&gt;&lt;br&gt;
Works seamlessly with Next.js, Remix, and any server-side rendering setup. No window is defined.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Touch + Mouse Support&lt;/strong&gt;&lt;br&gt;
Handles both mousedown and touchstart events — works perfectly on mobile devices too.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Tiny Bundle Size&lt;/strong&gt;&lt;br&gt;
Under 400 bytes gzipped. Your users will never notice it’s there.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Supports Multiple Refs&lt;/strong&gt;&lt;br&gt;
Need to track outside clicks across multiple elements? Supported out of the box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real World Example — Dropdown Menu&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useOutsideClick&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@kitsunechaos/use-outside-click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DropdownMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;useOutsideClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inline-block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Options ▾
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1px solid #eee&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Edit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Duplicate&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more cleanup logic. No more forgotten removeEventListener. Just clean, readable code.&lt;/p&gt;

&lt;p&gt;Before vs After**&lt;br&gt;
Before — raw boilerplate every time:**&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 10+ lines, every single component&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousedown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousedown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After — one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useOutsideClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installation&lt;/p&gt;

&lt;h2&gt;
  
  
  npm
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @kitsunechaos/use-outside-click
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  yarn
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @kitsunechaos/use-outside-click
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  pnpm
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add @kitsunechaos/use-outside-click
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Links&lt;br&gt;
📦 npm → &lt;a href="https://www.npmjs.com/package/@kitsunechaos/use-outside-click" rel="noopener noreferrer"&gt;npmjs.com/package/@kitsunechaos/use-outside-click&lt;/a&gt;&lt;br&gt;
🐙 GitHub → &lt;a href="https://github.com/kitsunechaos-labs/use-outside-click#readme" rel="noopener noreferrer"&gt;github.com/kitsunechaos-labs/use-outside-click&lt;br&gt;
&lt;/a&gt;If this saved you from writing that boilerplate one more time — drop a ⭐ on GitHub. It genuinely helps other developers discover it.&lt;/p&gt;

&lt;p&gt;Built with 🦊 by &lt;a href="https://www.kitsunechaos.com/" rel="noopener noreferrer"&gt;kitsunechaos&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>reacthooks</category>
      <category>npm</category>
      <category>node</category>
    </item>
  </channel>
</rss>
