<?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: Josh Gummersall</title>
    <description>The latest articles on DEV Community by Josh Gummersall (@standardlabs).</description>
    <link>https://dev.to/standardlabs</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%2F2940716%2F23765150-f5c3-4ffb-b6cd-a8891ba324d9.jpeg</url>
      <title>DEV Community: Josh Gummersall</title>
      <link>https://dev.to/standardlabs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/standardlabs"/>
    <language>en</language>
    <item>
      <title>Why migrate from Redux to React Query</title>
      <dc:creator>Josh Gummersall</dc:creator>
      <pubDate>Sat, 15 Mar 2025 15:17:28 +0000</pubDate>
      <link>https://dev.to/standardlabs/why-migrate-from-redux-to-react-query-f4i</link>
      <guid>https://dev.to/standardlabs/why-migrate-from-redux-to-react-query-f4i</guid>
      <description>&lt;p&gt;State management in React applications has evolved significantly over the years. For a long time, Redux was the go-to solution for managing state, offering a predictable, centralized store with actions and reducers to handle updates. However, while Redux provided structure, it also introduced complexity—boilerplate code, verbose action handling, and the need for additional middleware (like Redux Thunk) for async operations. Developers often found themselves writing a lot of extra code just to fetch and manage server data efficiently.&lt;/p&gt;

&lt;p&gt;Enter React Query, a modern alternative designed specifically for managing asynchronous state with minimal effort. React Query embraces the fact that server state has unique challenges, such as caching, background synchronization, and automatic refetching. It drastically simplifies the way data is fetched, stored, and updated, removing unnecessary complexity while improving efficiency. Beyond just a better API, React Query enhances developer experience (DX) by reducing boilerplate, improving performance, and making data-fetching logic more intuitive.&lt;/p&gt;

&lt;p&gt;Migrating from Redux to React Query isn’t just about swapping libraries—it’s about making state management simpler, more efficient, and more enjoyable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less Boilerplate, More Productivity
&lt;/h2&gt;

&lt;p&gt;One of the biggest frustrations with (particularly legacy) Redux is the sheer amount of code required to manage server state. Fetching data typically involves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Writing an action creator&lt;/li&gt;
&lt;li&gt;Dispatching the action&lt;/li&gt;
&lt;li&gt;Handling loading and error states in a reducer&lt;/li&gt;
&lt;li&gt;Storing the fetched data in the Redux store&lt;/li&gt;
&lt;li&gt;Selecting the data with a useSelector hook&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With React Query, this entire process is replaced by a single &lt;code&gt;useQuery&lt;/code&gt; or &lt;code&gt;useMutation&lt;/code&gt; hook. No more writing extra reducers, manually handling cache invalidation, or worrying about keeping data fresh—React Query does it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in Caching &amp;amp; Synchronization
&lt;/h2&gt;

&lt;p&gt;Redux alone doesn’t offer caching out of the box, meaning developers often have to manually manage re-fetching and updating data. With React Query, caching is automatic. It keeps track of fetched data, reuses it across components, and automatically refreshes it in the background when needed. This eliminates stale data problems and reduces unnecessary network requests, improving both performance and user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimized Performance
&lt;/h2&gt;

&lt;p&gt;Redux’s approach to state updates can lead to unnecessary re-renders, especially when dealing with deeply nested structures or large datasets. React Query optimizes re-fetching and re-renders by tracking when data changes and only updating components that depend on it. Additionally, it offers features like pagination, infinite scrolling, and optimistic updates out of the box—features that require extra work in Redux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better DX (Developer Experience)
&lt;/h2&gt;

&lt;p&gt;React Query significantly improves DX by providing a simpler, more declarative API. Instead of manually dispatching actions, managing reducers, and handling middleware, developers can focus on writing business logic instead of plumbing code. The built-in React Query DevTools are quite nice, too!&lt;/p&gt;

&lt;p&gt;By switching to React Query, developers spend less time managing state and more time building features. It’s a smarter, more efficient approach to state management that prioritizes performance, maintainability, and developer happiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to still use Redux
&lt;/h2&gt;

&lt;p&gt;While React Query is excellent for handling server state, Redux still has its place—especially for managing client state that isn’t tied to API data. Things like UI state (modals, sidebars, theme settings), complex local state (such as multi-step forms), and global state that doesn’t need to be fetched from a server are often better suited for Redux or even the Context API. Additionally, if your application already has a well-structured Redux implementation with selectors and memoization, switching to React Query might not always be necessary unless managing server state has become a significant pain point. In some cases, a hybrid approach works best—using React Query for API calls and caching while keeping Redux for UI-related state management. The key is to use the right tool for the job rather than forcing one solution to handle everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Migrating from Redux to React Query can significantly improve developer experience, efficiency, and performance by reducing boilerplate, automating caching, and simplifying state management. Instead of manually handling API calls, background synchronization, and cache invalidation, React Query takes care of it for you—allowing developers to focus on building features rather than managing state. While Redux still has its uses for client-side state, React Query is a powerful, modern alternative for handling server state with far less complexity. If you're looking for a faster, cleaner, and more efficient way to manage data in your React app, it’s time to give React Query a try.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>redux</category>
    </item>
    <item>
      <title>TailwindCSS &amp; DaisyUI in the Shadow DOM</title>
      <dc:creator>Josh Gummersall</dc:creator>
      <pubDate>Thu, 13 Mar 2025 20:51:24 +0000</pubDate>
      <link>https://dev.to/standardlabs/tailwindcss-daisyui-in-the-shadow-dom-oik</link>
      <guid>https://dev.to/standardlabs/tailwindcss-daisyui-in-the-shadow-dom-oik</guid>
      <description>&lt;p&gt;When injecting a React component into a third-party website—whether through a Chrome extension or a simple script—one of the biggest challenges is styling. CSS conflicts, unexpected overrides, and global styles can break your UI or make it look completely different from what you intended. The same is true of the host UI! The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM" rel="noopener noreferrer"&gt;Shadow DOM&lt;/a&gt; provides a powerful solution by encapsulating your component and isolating it from the host page’s styles.&lt;/p&gt;

&lt;p&gt;However, using popular styling frameworks like &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt; and &lt;a href="https://daisyui.com" rel="noopener noreferrer"&gt;DaisyUI&lt;/a&gt; inside the Shadow DOM isn’t straightforward. Since styles in the Shadow DOM don’t inherit from the global stylesheet, you need a strategy to ensure your component still benefits from Tailwind’s utility classes and DaisyUI’s prebuilt components.&lt;/p&gt;

&lt;p&gt;In this post, I'll walk you through the process of injecting a React component into the Shadow DOM while ensuring TailwindCSS and DaisyUI styles are properly applied. By the end, you'll have a working setup that you can use for script injection, Chrome extensions, or any scenario where you need a self-contained UI inside a third-party site.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Shadow DOM?
&lt;/h2&gt;

&lt;p&gt;The Shadow DOM is a web standard that allows developers to encapsulate HTML, CSS, and JavaScript within a self-contained scope, preventing styles and scripts from leaking in or out. This is particularly useful when injecting UI components into third-party websites, as it prevents conflicts with the site's existing styles and ensures your component looks and behaves consistently. Unlike the regular DOM, where styles can cascade globally, elements inside a Shadow DOM tree have their own isolated styles and scripts, making it ideal for building embeddable widgets, Chrome extensions, and injected scripts that need to maintain their design integrity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up React Component Injection
&lt;/h2&gt;

&lt;p&gt;To inject a React component into a third-party website, you first need to create a container element and attach it to the page. However, instead of simply appending a &lt;code&gt;div&lt;/code&gt; to &lt;code&gt;document.body&lt;/code&gt;, we’ll use the Shadow DOM to encapsulate our component and prevent style conflicts.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&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;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&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;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&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;This isolates everything we place inside shadowRoot from the rest of the page’s styles and scripts.&lt;/p&gt;

&lt;p&gt;Now, let’s render a simple React component inside the Shadow DOM using createRoot from React 18:&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;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&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;Component&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;
  
  
  What about Tailwind CSS and Daisy UI?
&lt;/h2&gt;

&lt;p&gt;We'll use &lt;a href="https://vite.dev" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; as a bundling solution as both Tailwind CSS and Daisy UI require importing CSS (the &lt;a href="https://tailwindcss.com/docs/installation/using-vite" rel="noopener noreferrer"&gt;Get started with Tailwind CSS&lt;/a&gt; guide provides details on using Vite). The &lt;code&gt;vite.config.ts&lt;/code&gt; file looks like:&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;defineConfig&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;vite&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;react&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;@vitejs/plugin-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;tailwindcss&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;@tailwindcss/vite&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;tailwindcss&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rollupOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Note: useful to produce a single JS file for a &amp;lt;script&amp;gt; tag&lt;/span&gt;
      &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;entryFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;chunkFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[name].js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;assetFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[name].[ext]&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="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;Next, using TailwindCSS 4 and DaisyUI 5, configuration is handled entirely in a single CSS file. TailwindCSS version 4 includes &lt;code&gt;:host&lt;/code&gt; in its CSS rules intended to apply to the entire DOM. Lucky for us, &lt;code&gt;:host&lt;/code&gt; is a CSS pseudo-selector targeting the Shadow DOM host!&lt;/p&gt;

&lt;p&gt;For DaisyUI, we just need to include the &lt;code&gt;root&lt;/code&gt; option as it defaults to &lt;code&gt;:root&lt;/code&gt; if unspecified. Our CSS file looks like:&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;@plugin&lt;/span&gt; &lt;span class="s1"&gt;"daisyui"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;":host"&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;Finally, our React Component must import this CSS file in a way that includes it in our bundled Javascript (rather than including it as a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag). This ensures the TailwindCSS and DaisyUI CSS rules only apply inside the Shadow DOM. It's also a better end-user experience for the person adding just one &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag to their HTML.&lt;/p&gt;

&lt;p&gt;Luckily, Vite provides a &lt;code&gt;?inline&lt;/code&gt; modifier for CSS imports that produces a style string:&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="nx"&gt;style&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;./index.css?inline&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="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Component&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;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;style&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;/style&lt;/span&gt;&lt;span class="err"&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;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="nx"&gt;world&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="o"&gt;&amp;lt;&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;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frd070dhlgflq1q6at7zo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frd070dhlgflq1q6at7zo.png" alt="Example webpage with Shadow DOM TailwindCSS and DaisyUI implementation" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Notice the DOM structure in the developer tools window, and notice the host page remains un-styled&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There you have it! With this setup, you have React, TailwindCSS, and DaisyUI running inside an isolated Shadow DOM. Styles are correctly applied inside the Shadow DOM, while the regular host page UI remains completely unchanged.&lt;/p&gt;

&lt;p&gt;For more details and a jumping off point, check out the &lt;a href="https://github.com/standardlabs/tailwind-daisy-shadow" rel="noopener noreferrer"&gt;Github repository&lt;/a&gt;. If you need any help with React or Javascript development, please &lt;a href="https://standardlabs.dev#get-in-touch" rel="noopener noreferrer"&gt;get in touch!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>tailwindcss</category>
    </item>
  </channel>
</rss>
