<?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: Kiyamov-Bulat</title>
    <description>The latest articles on Forem by Kiyamov-Bulat (@bulat).</description>
    <link>https://forem.com/bulat</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%2F3601528%2Fef8e9662-0459-48d7-ade2-6595d7e5a583.png</url>
      <title>Forem: Kiyamov-Bulat</title>
      <link>https://forem.com/bulat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bulat"/>
    <language>en</language>
    <item>
      <title>Building a Lightweight React Table with Per-Column Filtering</title>
      <dc:creator>Kiyamov-Bulat</dc:creator>
      <pubDate>Sat, 08 Nov 2025 08:58:41 +0000</pubDate>
      <link>https://forem.com/bulat/building-a-lightweight-react-table-with-per-column-filtering-15nc</link>
      <guid>https://forem.com/bulat/building-a-lightweight-react-table-with-per-column-filtering-15nc</guid>
      <description>&lt;h2&gt;
  
  
  ❗ The Problem
&lt;/h2&gt;

&lt;p&gt;Every time I started a new internal tool or admin panel, I had to rebuild the same data table:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sorting by column
&lt;/li&gt;
&lt;li&gt;filtering per field
&lt;/li&gt;
&lt;li&gt;responsive layout
&lt;/li&gt;
&lt;li&gt;TypeScript support
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Popular libraries like Ag-Grid or TanStack Table are powerful — but often &lt;strong&gt;overkill&lt;/strong&gt; for simple use cases.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;code&gt;@bulak/react-registry&lt;/code&gt;&lt;/strong&gt; — a minimal, fully typed React table component that does just enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Key Decisions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;No heavy dependencies&lt;/strong&gt; — only React and TypeScript&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Column sorting&lt;/strong&gt; — click a column header to sort&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Per-column filtering&lt;/strong&gt; — click the icon in the header to filter that field&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Easy to customize&lt;/strong&gt; — clean, modular code&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Smart + dumb components&lt;/strong&gt; — use &lt;code&gt;&amp;lt;Registry&amp;gt;&lt;/code&gt; for quick setup or &lt;code&gt;&amp;lt;Table&amp;gt;&lt;/code&gt; for full control
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;MIT licensed&lt;/strong&gt; — free for commercial use&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔴 Live Demo
&lt;/h2&gt;

&lt;p&gt;See it in action: &lt;a href="https://react-registry-azure.vercel.app" rel="noopener noreferrer"&gt;https://react-registry-azure.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 How to Use
&lt;/h2&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&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; @bulak/react-registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Basic usage (&lt;strong&gt;Registry&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RegistryHeader&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="s1"&gt;@bulak/react-registry&lt;/span&gt;&lt;span class="dl"&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;DATA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Harry Potter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;employeeNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&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;HEADERS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RegistryHeader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="kr"&gt;number&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fullName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calc(50% - 26px)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Full name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;employeeNumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calc(50% - 26px)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Employee number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;50px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Registry&lt;/span&gt;
            &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bordered&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;sortable&lt;/span&gt;&lt;span class="o"&gt;=&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="nx"&gt;filterable&lt;/span&gt;&lt;span class="o"&gt;=&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="sr"&gt;/&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;&lt;em&gt;💡 For full control, use the Table compound component and utility hooks (see docs).&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  💻 Open Source
&lt;/h2&gt;

&lt;p&gt;📦 npm: &lt;a href="https://www.npmjs.com/package/@bulak/react-registry" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@bulak/react-registry&lt;/a&gt;&lt;br&gt;
🐙 GitHub: &lt;a href="https://github.com/bulak/react-registry" rel="noopener noreferrer"&gt;https://github.com/bulak/react-registry&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ❓ Why Not TanStack Table?
&lt;/h2&gt;

&lt;p&gt;I love TanStack Table — but it only provides logic, not UI.&lt;br&gt;
I wanted a ready-to-use component with styles, filtering UI, and TypeScript out of the box.&lt;/p&gt;
&lt;h2&gt;
  
  
  ❓ What caused you to write this?
&lt;/h2&gt;

&lt;p&gt;At my job, I make a lot of tables of the same type. And I thought it would be a good idea to put all my knowledge and experience in one package. At work, we mostly made our own UI kit, with almost no dependencies. Our tables have many functions, from resizing and moving columns to exporting. In fact, we are slowly making our own excel haha. As a result, in addition to expressing my experience, I want to implement all these functions in my package, plus structure and refactor the code, in such a way that it could be useful to others.&lt;/p&gt;

&lt;p&gt;It’s MIT licensed — use it anywhere. Feedback and PRs welcome!&lt;/p&gt;
&lt;h2&gt;
  
  
  🏁 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Sometimes, the best solution is a small, focused component that solves your specific problem — without the bloat.&lt;/p&gt;

&lt;p&gt;Give it a try — and let me know what you think!&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__tag ltag__tag__id__125"&gt;
    &lt;div class="ltag__tag__content"&gt;
      &lt;h2&gt;#&lt;a href="https://dev.to/t/react" class="ltag__tag__link"&gt;react&lt;/a&gt; Follow
&lt;/h2&gt;
      &lt;div class="ltag__tag__summary"&gt;
        Official tag for Facebook's React JavaScript library for building user interfaces
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





</description>
      <category>showdev</category>
      <category>typescript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
