<?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: BPDM 🧩⚡</title>
    <description>The latest articles on DEV Community by BPDM 🧩⚡ (@bpdm9).</description>
    <link>https://dev.to/bpdm9</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047085%2Fa8bd24ea-1027-4a21-bdef-9cea52247d03.png</url>
      <title>DEV Community: BPDM 🧩⚡</title>
      <link>https://dev.to/bpdm9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bpdm9"/>
    <language>en</language>
    <item>
      <title>Every realtime feature I shipped turned into socket plumbing, so I built Liveflux</title>
      <dc:creator>BPDM 🧩⚡</dc:creator>
      <pubDate>Thu, 06 Aug 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/bpdm9/every-realtime-feature-i-shipped-turned-into-socket-plumbing-so-i-built-liveflux-8pk</link>
      <guid>https://dev.to/bpdm9/every-realtime-feature-i-shipped-turned-into-socket-plumbing-so-i-built-liveflux-8pk</guid>
      <description>&lt;p&gt;At work, every realtime feature starts the same way: open a WebSocket, push messages into state, render a list. Ten minutes, done. Then it meets production.&lt;/p&gt;

&lt;p&gt;The socket drops on flaky train wifi and never comes back. Two components mount the same feed and open two sockets. A burst of messages janks the main thread. A fast re-render tears the list mid-update. The ten-minute feature is now a week of socket plumbing: reconnect backoff, dedup, backpressure, tear-free reads. None of which was the feature.&lt;/p&gt;

&lt;p&gt;I'd written that plumbing enough times to want it to live in one place. So I built Liveflux.&lt;/p&gt;

&lt;h2&gt;
  
  
  You describe the fold; it owns the socket
&lt;/h2&gt;

&lt;p&gt;The shift is to stop thinking in "messages" and start thinking in "a reducer over a stream." You say which channel to subscribe to and how each event folds into the state your component renders. Liveflux owns the connection.&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;useStream&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;@liveflux/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Trade&lt;/span&gt; &lt;span class="o"&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;price&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Trades&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// upsert -&amp;gt; Trade[]: a matching id updates in place, a new id is appended.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Trade&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trades&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upsert&lt;/span&gt;&lt;span class="dl"&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;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cap&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="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&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;Row&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole component. The fold strategies cover most UIs: &lt;code&gt;append&lt;/code&gt; (a log), &lt;code&gt;upsert&lt;/code&gt; (a keyed list), &lt;code&gt;replace&lt;/code&gt; (the latest value), or your own reducer.&lt;/p&gt;

&lt;p&gt;Because the library owns the connection, the hard parts are handled once, for every subscription:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On an unexpected close it backs off with jitter and replays every active subscription, so streams resume on their own.&lt;/li&gt;
&lt;li&gt;Identical subscriptions share one socket, ref-counted; it closes when the last subscriber leaves.&lt;/li&gt;
&lt;li&gt;Oversized inbound frames are dropped before decoding, so a burst can't lock the UI.&lt;/li&gt;
&lt;li&gt;Reads go through &lt;code&gt;useSyncExternalStore&lt;/code&gt;, so state stays tear-free under concurrent rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One engine, five transports
&lt;/h2&gt;

&lt;p&gt;The engine is transport-agnostic. Point it at whatever your backend speaks by swapping the adapter, and the component doesn't change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@liveflux/ws&lt;/code&gt; for any plain WebSocket&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@liveflux/sse&lt;/code&gt; for Server-Sent Events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@liveflux/socketio&lt;/code&gt; for Socket.IO&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@liveflux/graphql-ws&lt;/code&gt; for GraphQL subscriptions over WebSocket&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@liveflux/phoenix&lt;/code&gt; for Phoenix Channels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core is framework-agnostic too; React is the first binding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest status
&lt;/h2&gt;

&lt;p&gt;It's pre-alpha and it's a solo project, so the API is still settling. But it's typed end-to-end and tree-shakeable, and every adapter is covered by a shared conformance suite plus real-server end-to-end tests (a real SSE server, a real Socket.IO server, a real graphql-ws server). The reconnect, dedup, and backpressure behaviour is actually exercised, not just claimed.&lt;/p&gt;

&lt;p&gt;If you build realtime UIs, I'd really like to know where this holds up and where it breaks in a real app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Have a look
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Site and live demo: &lt;a href="https://liveflux.bpdm.dev" rel="noopener noreferrer"&gt;https://liveflux.bpdm.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs (getting started, concepts): &lt;a href="https://liveflux.bpdm.dev/docs" rel="noopener noreferrer"&gt;https://liveflux.bpdm.dev/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/liveflux/liveflux" rel="noopener noreferrer"&gt;https://github.com/liveflux/liveflux&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Scaffold one: &lt;code&gt;pnpm create liveflux@latest&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>websockets</category>
    </item>
    <item>
      <title>🧩 One design system, native to both React and Angular</title>
      <dc:creator>BPDM 🧩⚡</dc:creator>
      <pubDate>Tue, 28 Jul 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/bpdm9/one-design-system-native-to-both-react-and-angular-4inn</link>
      <guid>https://dev.to/bpdm9/one-design-system-native-to-both-react-and-angular-4inn</guid>
      <description>&lt;p&gt;We run a React app and an Angular admin panel at work. Same company, same brand, and on paper the same design.&lt;/p&gt;

&lt;p&gt;On screen it was a different story. The React button had a 6px radius; the Angular one had 4px. The focus rings were two slightly different blues. Nobody noticed until somebody did. And every time design changed a token, someone got to hand-port it into two codebases. Twice the work, and it still drifted.&lt;/p&gt;

&lt;p&gt;So I went looking for something that treated both frameworks as equals. The React kits don't speak Angular. The Angular ones don't share a look with anything on the React side. Nothing let me define the design once and have it show up, the same, in both. So I built &lt;code&gt;bpdm/ui&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One rule: the look lives in tokens
&lt;/h2&gt;

&lt;p&gt;I gave myself one hard rule: nothing about how a component looks is allowed to live inside the React or Angular code. Colour, spacing, radius, the easing on transitions, all of it sits in &lt;code&gt;@bpdm/tokens&lt;/code&gt; as plain CSS variables, and both framework packages just read from there. The component owns structure, behaviour, and the accessibility plumbing. The look comes from the tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"@bpdm/tokens/tokens.css"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change one token and both frameworks move together. There's no "now go sync the Angular theme" step, because there's only one theme to sync. Four ship in the box (two light, two dark). Override the variables and you've re-skinned all of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same component, twice
&lt;/h2&gt;

&lt;p&gt;React:&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;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Badge&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;@bpdm/ui&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;Example&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Get started &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Badge&lt;/span&gt; &lt;span class="na"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"soft"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;New&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Badge&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="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;Angular:&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;Component&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;@angular/core&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BpdmButton&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;@bpdm/ng&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BpdmButton&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;button bpdmButton&amp;gt;Get started&amp;lt;/button&amp;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;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same padding, same radius, same focus ring. The accessibility isn't literally shared code: Radix does that work on the React side, the Angular CDK on the Angular side. But both follow the same ARIA patterns, so keyboard and screen-reader behaviour ends up matching instead of being reinvented twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it's at
&lt;/h2&gt;

&lt;p&gt;38 components, both frameworks. I started with the stuff dashboards and back-office tools actually lean on, the pieces I kept rebuilding by hand: money inputs, masked and secure fields, KPI stat cards, status timelines, multi-selects, tree-selects. It's all typed and tree-shakeable, MIT-licensed.&lt;/p&gt;

&lt;p&gt;I'll be straight about what it isn't: it's young. It hasn't survived years in production yet, and the Angular side hasn't taken the same beating the React one has. If you try it and something feels even slightly off between the two frameworks, that's the bug I want to hear about first. Parity is the whole reason this exists, so a gap there means I got the one thing wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few things I traded away on purpose
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It needs Tailwind v4. Not using Tailwind? Then it's not your library, and that's fine.&lt;/li&gt;
&lt;li&gt;One shared token set means less room to go off-script in a single framework. That was the trade I wanted: consistency over local freedom.&lt;/li&gt;
&lt;li&gt;It's a solo project. If you're weighing it for something serious, that's a real risk, and I'd rather say so than hide it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Have a look
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live components and themes: &lt;a href="https://ui.bpdm.dev" rel="noopener noreferrer"&gt;https://ui.bpdm.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs (install, theming, per-component API): &lt;a href="https://docs.ui.bpdm.dev" rel="noopener noreferrer"&gt;https://docs.ui.bpdm.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source, both libraries: &lt;a href="https://github.com/bpdm-hq/bpdm-ui" rel="noopener noreferrer"&gt;https://github.com/bpdm-hq/bpdm-ui&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;On npm: &lt;a href="https://www.npmjs.com/package/@bpdm/ui" rel="noopener noreferrer"&gt;&lt;code&gt;@bpdm/ui&lt;/code&gt;&lt;/a&gt; (React), &lt;a href="https://www.npmjs.com/package/@bpdm/ng" rel="noopener noreferrer"&gt;&lt;code&gt;@bpdm/ng&lt;/code&gt;&lt;/a&gt; (Angular)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work across React and Angular, I'd really like to know whether the parity holds up in a real codebase and not just in my demos. That's the thing I most want to find out.&lt;/p&gt;

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