<?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>DEV Community: Bassem Chagra</title>
    <description>The latest articles on DEV Community by Bassem Chagra (@ch-bas).</description>
    <link>https://dev.to/ch-bas</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%2F3754597%2F9018a1d4-ec26-41fd-8326-44d989980306.png</url>
      <title>DEV Community: Bassem Chagra</title>
      <link>https://dev.to/ch-bas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ch-bas"/>
    <language>en</language>
    <item>
      <title>Migrating from Elastic EUI to MUI, Chakra, or Ant Design — The Complete Playbook</title>
      <dc:creator>Bassem Chagra</dc:creator>
      <pubDate>Tue, 14 Apr 2026 10:52:09 +0000</pubDate>
      <link>https://dev.to/ch-bas/migrating-from-elastic-eui-to-mui-chakra-or-ant-design-the-complete-playbook-3l2f</link>
      <guid>https://dev.to/ch-bas/migrating-from-elastic-eui-to-mui-chakra-or-ant-design-the-complete-playbook-3l2f</guid>
      <description>&lt;p&gt;If you're reading this, your team probably built something on Elastic EUI and now you're stuck. Maybe you're not an Elastic shop anymore. Maybe the 188kb bundle is killing your Lighthouse scores. Maybe you're tired of fighting the Elastic License.&lt;/p&gt;

&lt;p&gt;Whatever the reason, you need to migrate — and the docs won't help you. This is what I learned mapping &lt;strong&gt;45 EUI components&lt;/strong&gt; to their MUI, Chakra, and Ant Design equivalents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why teams leave EUI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bundle size.&lt;/strong&gt; EUI ships approximately 188KB gzipped. MUI's tree-shakeable architecture lets you import only what you use, typically resulting in 40-60% smaller bundles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Elastic ecosystem lock-in.&lt;/strong&gt; EUI was designed for Kibana. Its design language, component behavior, and even its color palette reflect Elastic's brand. Teams building non-Elastic apps find themselves fighting the library's opinions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;License concerns.&lt;/strong&gt; EUI uses the Elastic License 2.0 (ELv2), which is not OSI-approved. Some legal teams flag this as a compliance risk for SaaS products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Community.&lt;/strong&gt; Stack Overflow questions, blog posts, and community plugins are significantly fewer compared to MUI (93k+ GitHub stars) or Ant Design (91k+).&lt;/p&gt;

&lt;h2&gt;
  
  
  The hardest parts nobody warns you about
&lt;/h2&gt;

&lt;h3&gt;
  
  
  EuiProvider is a monolith
&lt;/h3&gt;

&lt;p&gt;EUI wraps theme, i18n, toast notifications, and global component defaults in a single provider. You can't extract components one-by-one without either keeping &lt;code&gt;EuiProvider&lt;/code&gt; alive (bloating the bundle) or breaking everything at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Map every component that depends on &lt;code&gt;EuiProvider&lt;/code&gt; context before writing a single line of migration code. You'll be surprised how many "independent" components break without it.&lt;/p&gt;

&lt;h3&gt;
  
  
  EuiBasicTable pagination is invisible
&lt;/h3&gt;

&lt;p&gt;EUI manages pagination state internally. MUI's DataGrid requires you to manage &lt;code&gt;paginationModel&lt;/code&gt; state yourself — page index, page size, total count. Teams that relied on EUI's "just pass items and it works" discover they need to add 20+ lines of state management.&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="c1"&gt;// EUI — pagination is automatic&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EuiBasicTable&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// MUI — you manage the state&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;paginationModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPaginationModel&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="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataGrid&lt;/span&gt;
  &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;paginationModel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paginationModel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onPaginationModelChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setPaginationModel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;pageSizeOptions&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="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Color tokens are lossy
&lt;/h3&gt;

&lt;p&gt;EUI uses semantic color names (&lt;code&gt;subdued&lt;/code&gt;, &lt;code&gt;accent&lt;/code&gt;, &lt;code&gt;success&lt;/code&gt;) that map to its own palette. MUI uses Material Design colors (&lt;code&gt;primary&lt;/code&gt;, &lt;code&gt;secondary&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;). There's no 1:1 mapping — &lt;code&gt;subdued&lt;/code&gt; has no MUI equivalent. You'll end up building a custom theme layer just to bridge the gap.&lt;/p&gt;

&lt;h3&gt;
  
  
  The date math dependency
&lt;/h3&gt;

&lt;p&gt;EUI depends on &lt;code&gt;@elastic/datemath&lt;/code&gt; and &lt;code&gt;moment&lt;/code&gt;. If your app uses &lt;code&gt;EuiSuperDatePicker&lt;/code&gt;'s relative date expressions ("now-15m"), you need to keep these dependencies or rewrite the date parsing logic. MUI doesn't have anything equivalent.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript migration patterns
&lt;/h2&gt;

&lt;p&gt;This is where senior engineers spend most of their time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generic type mapping (Data Tables)
&lt;/h3&gt;

&lt;p&gt;EUI uses generics to enforce type safety on row items:&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="c1"&gt;// EUI&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EuiBasicTableColumn&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;@elastic/eui&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;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EuiBasicTableColumn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;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;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&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="na"&gt;sortable&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EuiHealth&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;danger&lt;/span&gt;&lt;span class="dl"&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;EuiHealth&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;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MUI equivalent&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GridColDef&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;@mui/x-data-grid&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;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GridColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&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;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headerName&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="na"&gt;sortable&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;renderCell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;row&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Chip&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"small"&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;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key differences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;EuiBasicTableColumn&amp;lt;T&amp;gt;&lt;/code&gt; → &lt;code&gt;GridColDef&amp;lt;T&amp;gt;&lt;/code&gt; — different generic wrapper&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; → &lt;code&gt;headerName&lt;/code&gt; — column display name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;render: (value)&lt;/code&gt; → &lt;code&gt;renderCell: ({ row })&lt;/code&gt; — receives params object, not raw value&lt;/li&gt;
&lt;li&gt;MUI DataGrid requires &lt;code&gt;@mui/x-data-grid&lt;/code&gt; as a separate package&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Dealing with ExclusiveUnion
&lt;/h3&gt;

&lt;p&gt;EUI uses a custom &lt;code&gt;ExclusiveUnion&lt;/code&gt; utility to strictly prevent mixing button/anchor props:&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="c1"&gt;// EUI — TypeScript Error! Can't have both onClick and href&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EuiButton&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&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="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;EuiButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MUI handles this with the &lt;code&gt;component&lt;/code&gt; prop:&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="c1"&gt;// MUI — explicit about the underlying element&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&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="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// Next.js Link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When migrating wrapper components, rewrite using MUI's typed component pattern:&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="c1"&gt;// Before: EUI wrapper with ExclusiveUnion&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EuiButtonProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// includes ExclusiveUnion internally&lt;/span&gt;

&lt;span class="c1"&gt;// After: MUI wrapper with generic component type&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ElementType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The 10 components with no direct equivalent
&lt;/h2&gt;

&lt;p&gt;These will cost you the most time:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;EUI Component&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiSuperDatePicker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build custom with two MUI DatePickers + relative date presets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiGlobalToastList&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use notistack or build a custom Snackbar queue manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiHealth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trivial — just a colored dot with &lt;code&gt;Box&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiStat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build with &lt;code&gt;Typography&lt;/code&gt; + &lt;code&gt;Box&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiColorPicker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use react-colorful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiMarkdownEditor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use @uiw/react-md-editor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiCodeBlock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use react-syntax-highlighter or prism-react-renderer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;EuiComboBox&lt;/code&gt; (full)&lt;/td&gt;
&lt;td&gt;MUI Autocomplete covers 80% — async loading needs custom work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;EuiContextMenu&lt;/code&gt; (panels)&lt;/td&gt;
&lt;td&gt;MUI Menu doesn't support panel-based navigation — build custom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EuiSelectable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No direct equivalent — use custom interface with MUI Select&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The migration strategy that works
&lt;/h2&gt;

&lt;p&gt;After doing this across 45 components, here's the approach I'd use again:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Audit.&lt;/strong&gt; List every EUI component in your codebase. Count usage. Sort by frequency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Abstraction layer.&lt;/strong&gt; Create wrapper components that currently re-export EUI:&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="c1"&gt;// /components/ui/Button.tsx — Phase 1&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EuiButton&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Button&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;@elastic/eui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Phase 2 — swap the import, consumers don't change&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&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;@mui/material&lt;/span&gt;&lt;span class="dl"&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;strong&gt;Step 3: Leaf components first.&lt;/strong&gt; Migrate buttons, badges, text, avatars — the components that don't depend on others. This builds confidence and momentum.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Complex components last.&lt;/strong&gt; Tables, modals, forms — these touch the most code and have the most API differences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Kill EuiProvider.&lt;/strong&gt; Once all components are migrated, remove &lt;code&gt;EuiProvider&lt;/code&gt; and &lt;code&gt;@elastic/eui&lt;/code&gt; from your dependencies. Verify nothing breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive component reference
&lt;/h2&gt;

&lt;p&gt;I built a searchable tool that maps all 45 EUI components to their MUI equivalents — including props, import changes, and which ones have no direct equivalent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://frontfamily.com/guides/eui-migration" rel="noopener noreferrer"&gt;Try the interactive prop search table →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can also paste EUI code directly into the converter and get MUI/Chakra/Ant Design output instantly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://frontfamily.com/converter?source=react-eui&amp;amp;target=react-mui" rel="noopener noreferrer"&gt;Open the converter →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or eject a production-ready component into your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @frontfamily/cli eject data-table &lt;span class="nt"&gt;-f&lt;/span&gt; react-mui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;This guide is part of &lt;a href="https://frontfamily.com" rel="noopener noreferrer"&gt;FrontFamily&lt;/a&gt; — a free tool for converting UI components between frameworks. 42 conversion paths, 219 verified component mappings, 18 ejectable patterns.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>migration</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Nobody Teaches You How to Build Kibana Plugins. So I Packaged Everything I Know</title>
      <dc:creator>Bassem Chagra</dc:creator>
      <pubDate>Thu, 05 Feb 2026 10:00:24 +0000</pubDate>
      <link>https://dev.to/ch-bas/nobody-teaches-you-how-to-build-kibana-plugins-so-i-packaged-everything-i-know-1g7f</link>
      <guid>https://dev.to/ch-bas/nobody-teaches-you-how-to-build-kibana-plugins-so-i-packaged-everything-i-know-1g7f</guid>
      <description>&lt;h2&gt;
  
  
  I Built a Tool That Does My Job (And I'm Giving It Away for Free)
&lt;/h2&gt;

&lt;p&gt;Last month I caught myself explaining how to set up Kibana plugin authentication for the fourth time. Same patterns. Same mistakes the other developer was about to make. Same "oh wait, you also need to handle multi-tenancy" moment 20 minutes in.&lt;/p&gt;

&lt;p&gt;I thought: what if I just... packaged everything I know into something reusable?&lt;/p&gt;

&lt;p&gt;So I built a Claude Code plugin for Kibana development. It's on GitHub. It's free. And honestly, I'm not sure if this was smart or if I just automated myself out of a job.&lt;/p&gt;

&lt;p&gt;Here's the story.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Try Googling "how to build a Kibana external plugin."&lt;/p&gt;

&lt;p&gt;Go ahead. I'll wait.&lt;/p&gt;

&lt;p&gt;You'll find Elastic's official docs (sparse), a deprecated Yeoman generator from 2018, and a bunch of Stack Overflow posts from people who gave up. That's basically it.&lt;/p&gt;

&lt;p&gt;When I started building Kibana plugins at UNIBERG, my main learning resource was... reading Kibana's source code. The actual Kibana repo. 200,000+ files. Searching for patterns. Hoping the internal plugin I was looking at was doing things the "right" way and not some legacy hack that hadn't been cleaned up yet.&lt;/p&gt;

&lt;p&gt;There's no tutorial. No course. No "Kibana Plugin Development for Dummies." You just figure it out.&lt;/p&gt;

&lt;p&gt;Which is fine — I actually enjoy that kind of thing. But after 6+ years of figuring it out, I had a lot of knowledge sitting in my head (and scattered across about 40 different &lt;code&gt;notes.md&lt;/code&gt; files) that could save someone months of pain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why a Claude Code Plugin?
&lt;/h2&gt;

&lt;p&gt;I'd been using Claude Code for a while. One day I looked at the plugin docs and realized the structure was weirdly similar to what I already knew from Kibana plugins. Manifest file. Directory structure. Configuration. Except way simpler.&lt;/p&gt;

&lt;p&gt;Claude Code plugins are basically: a JSON manifest, some markdown files for commands/skills/agents, and that's it. No compiled code. No build step.&lt;/p&gt;

&lt;p&gt;My first thought: "I could put my entire Kibana knowledge base into a skill file and Claude would just... know it."&lt;/p&gt;

&lt;p&gt;My second thought: "That can't actually work."&lt;/p&gt;

&lt;p&gt;It did.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Actually Built
&lt;/h2&gt;

&lt;p&gt;Started on a Saturday afternoon. Figured it'd take a couple hours.&lt;/p&gt;

&lt;p&gt;It took two full days. And then another week of adding stuff because I kept thinking "oh, and this pattern too."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The plugin has:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;10 slash commands — &lt;code&gt;/scaffold&lt;/code&gt; generates a full plugin skeleton (server + public + types + manifest). &lt;code&gt;/route&lt;/code&gt; creates server routes with proper &lt;code&gt;@kbn/config-schema&lt;/code&gt; validation. &lt;code&gt;/component&lt;/code&gt; generates EUI-based React components. &lt;code&gt;/security&lt;/code&gt; sets up RBAC. And a few more.&lt;/p&gt;

&lt;p&gt;6 specialized agents — an architect that reviews your plugin structure, a security auditor that checks for auth bypass and data leaks, a migration assistant for version upgrades.&lt;/p&gt;

&lt;p&gt;1 massive skill file — around 400 lines of concentrated Kibana plugin knowledge. Plugin lifecycle. Route patterns. ES client usage. EUI components. Auth middleware. Testing patterns. Common pitfalls.&lt;/p&gt;

&lt;p&gt;The skill file is the part I'm most proud of. And also the part that took the longest because I kept going "wait, I forgot about saved object migrations" and adding another section.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stuff That Was Hard to Get Right
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Route authentication patterns.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This sounds boring. It is boring. But if you get it wrong, you leak user data across tenants. I've seen it happen. I've caused it to happen. The plugin bakes security checks into every generated route by default, because I learned the hard way that "I'll add auth later" means "I'll discover the auth bug in production."&lt;/p&gt;

&lt;p&gt;Every route the &lt;code&gt;/route&lt;/code&gt; command generates starts with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;security&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrentUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forbidden&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boring. But I have scars from the time it wasn't there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EUI component patterns.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Elastic UI has maybe 80+ components. Some are well-documented. Some aren't. Some have props that don't do what you'd think. I spent a full day once debugging why an &lt;code&gt;EuiBasicTable&lt;/code&gt; wasn't rendering pagination correctly — turned out I was passing &lt;code&gt;totalItemCount&lt;/code&gt; as a string instead of a number. TypeScript didn't catch it because the prop type was &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/component&lt;/code&gt; command generates components that already handle the edge cases I've hit. Dark mode compatibility. Responsive layouts. Loading states. Error boundaries. Stuff I used to forget and then fix in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Elasticsearch client integration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Kibana ES client isn't the same as the regular &lt;code&gt;@elastic/elasticsearch&lt;/code&gt; client. It's wrapped. It has different error types. It handles auth differently. And it changes between Kibana minor versions in ways that aren't always documented.&lt;/p&gt;

&lt;p&gt;I wrote a service layer pattern into the skill that handles type coercion, error mapping, and defensive defaults for every ES response. It's about 200 lines of code I never want to look at again. But every plugin I build copies it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Part Where I Question Everything
&lt;/h2&gt;

&lt;p&gt;Here's what's weird about open-sourcing your expertise: someone could install this plugin and skip years of learning.&lt;/p&gt;

&lt;p&gt;The auth patterns I figured out through a security audit that almost gave me a heart attack? It's in there.&lt;/p&gt;

&lt;p&gt;The EUI dark mode bug that cost me a full Wednesday? Handled automatically.&lt;/p&gt;

&lt;p&gt;The Elasticsearch response transformation layer I wrote at 2 AM because production data had timestamps in three different formats? Built into the skill.&lt;/p&gt;

&lt;p&gt;Am I devaluing my own experience by giving it away?&lt;/p&gt;

&lt;p&gt;Maybe. Probably not. The plugin can scaffold code and teach patterns, but it can't debug your specific production issue at 11 PM on a Thursday. It can't look at your data model and say "this won't scale past 100k records." It can't sit in a meeting and explain to a product manager why multi-tenancy takes three weeks, not three days.&lt;/p&gt;

&lt;p&gt;Tools don't replace experience. They just make the boring parts faster.&lt;/p&gt;

&lt;p&gt;At least that's what I tell myself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Happens When You Use It
&lt;/h2&gt;

&lt;p&gt;Here's a real example. You type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/scaffold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude asks what you're building. You say "user management plugin with RBAC."&lt;/p&gt;

&lt;p&gt;Two minutes later you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plugin manifest with correct Kibana version targeting&lt;/li&gt;
&lt;li&gt;Server-side setup with route registration&lt;/li&gt;
&lt;li&gt;Public-side app mounting with React&lt;/li&gt;
&lt;li&gt;TypeScript types for your domain&lt;/li&gt;
&lt;li&gt;Basic EUI page layout with navigation&lt;/li&gt;
&lt;li&gt;Security configuration with feature privileges&lt;/li&gt;
&lt;li&gt;A working &lt;code&gt;kibana.jsonc&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before this plugin, that setup took me about 4 hours. And I'd still forget something — usually the &lt;code&gt;kibana.jsonc&lt;/code&gt; configuration, because that file is easy to get wrong and hard to debug when you do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Things That Are Still Missing
&lt;/h2&gt;

&lt;p&gt;I'll be honest about what's not in there yet:&lt;/p&gt;

&lt;p&gt;State management patterns — I have opinions (use React context + custom hooks, not Redux) but haven't formalized them into commands yet.&lt;/p&gt;

&lt;p&gt;Logging and monitoring — This is important for production plugins and I keep putting it off because it's not the fun part.&lt;/p&gt;

&lt;p&gt;Inter-plugin dependencies — This is genuinely complicated. Kibana's dependency injection system between plugins is powerful but also confusing. I've been working with it for years and still double-check the docs every time.&lt;/p&gt;

&lt;p&gt;Real-time data patterns — WebSocket-style updates through Kibana. I've done it, but the patterns aren't clean enough to template yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Open Source Anxiety
&lt;/h2&gt;

&lt;p&gt;Publishing it was uncomfortable.&lt;/p&gt;

&lt;p&gt;Not because of the code — the code is fine. But because it's basically my brain in a markdown file. Every pattern is something I learned through mistakes. Every anti-pattern check in the security agent is a bug I caused or caught.&lt;/p&gt;

&lt;p&gt;What if someone finds something wrong? What if a pattern that works in my use cases breaks in theirs? What if an Elastic engineer looks at it and says "that's not how we do it internally"?&lt;/p&gt;

&lt;p&gt;Then... they'll open an issue. And I'll fix it. Or learn something new.&lt;/p&gt;

&lt;p&gt;That's kind of the point.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Actually Use It
&lt;/h2&gt;

&lt;p&gt;If you're building Kibana plugins (or thinking about it):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add ch-bas/kibana-plugin-helper
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;kibana-plugin-helper@kibana-plugin-helper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then try &lt;code&gt;/scaffold&lt;/code&gt; and see what happens.&lt;/p&gt;

&lt;p&gt;If you're not building Kibana plugins — honestly, most people aren't — this post is really about something else: packaging your hard-won knowledge into something others can use.&lt;/p&gt;

&lt;p&gt;Whatever your niche is, you have patterns and lessons that could save someone else months. The format doesn't matter. A plugin, a blog post, a markdown file in a GitHub repo. The point is getting it out of your head and into a place where it helps.&lt;/p&gt;

&lt;p&gt;I just happened to pick a Claude Code plugin because the structure was familiar and I'm a developer, so of course I over-engineered it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Like to Know
&lt;/h2&gt;

&lt;p&gt;If you're doing Kibana plugin development: what's the thing you wish someone had told you on day one? I might add it.&lt;/p&gt;

&lt;p&gt;If you've open-sourced your expertise before: did it help your career or hurt it? Genuinely curious.&lt;/p&gt;

&lt;p&gt;And if you've used Claude Code plugins: are people actually discovering them through marketplaces, or is it still mostly word of mouth?&lt;/p&gt;

&lt;p&gt;Drop your answers. I read everything.&lt;/p&gt;




&lt;p&gt;GitHub: &lt;a href="//github.com/ch-bas/kibana-plugin-helper"&gt;github.com/ch-bas/kibana-plugin-helper&lt;/a&gt;&lt;br&gt;
License: MIT (do whatever you want with it)&lt;/p&gt;

&lt;h1&gt;
  
  
  Kibana #ElasticStack #OpenSource #ClaudeCode #WebDevelopment #React
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
