<?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: Shanuka Hettiarachchi</title>
    <description>The latest articles on DEV Community by Shanuka Hettiarachchi (@shanukj).</description>
    <link>https://dev.to/shanukj</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%2F551365%2Ff117a0de-410b-4e2e-ae2d-365a08bbce91.png</url>
      <title>DEV Community: Shanuka Hettiarachchi</title>
      <link>https://dev.to/shanukj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shanukj"/>
    <language>en</language>
    <item>
      <title>🚀 Stop Writing Separate Skeleton Components in React</title>
      <dc:creator>Shanuka Hettiarachchi</dc:creator>
      <pubDate>Tue, 17 Feb 2026 06:14:37 +0000</pubDate>
      <link>https://dev.to/shanukj/stop-writing-separate-skeleton-components-in-react-52b0</link>
      <guid>https://dev.to/shanukj/stop-writing-separate-skeleton-components-in-react-52b0</guid>
      <description>&lt;p&gt;Every time we build loading states in React, we usually end up doing something like this:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserProfileSkeleton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserProfile&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;Now we have:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UserProfile&lt;/code&gt;&lt;br&gt;
&lt;code&gt;UserProfileSkeleton&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two components.&lt;/li&gt;
&lt;li&gt;Two layouts.&lt;/li&gt;
&lt;li&gt;Two things to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And when the design changes?&lt;/p&gt;

&lt;p&gt;You update both.&lt;/p&gt;

&lt;p&gt;That’s duplicated UI logic.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Real Problem
&lt;/h2&gt;

&lt;p&gt;Skeleton screens are great for UX.&lt;br&gt;
But maintaining them manually is painful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout changes → update skeleton&lt;/li&gt;
&lt;li&gt;Add a new field → update skeleton&lt;/li&gt;
&lt;li&gt;Adjust spacing → update skeleton&lt;/li&gt;
&lt;li&gt;Refactor component → update skeleton&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It becomes repetitive and fragile.&lt;/p&gt;

&lt;p&gt;What if we could generate skeleton screens automatically from the existing DOM structure?&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing AutoSkeleton
&lt;/h2&gt;

&lt;p&gt;auto-skeleton-react lets you wrap any component and automatically generate a matching skeleton layout.&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AutoSkeleton&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&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="nc"&gt;UserProfile&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;AutoSkeleton&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;That’s it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No separate skeleton component.&lt;/li&gt;
&lt;li&gt;No manual width/height tweaking.&lt;/li&gt;
&lt;li&gt;No duplicated layout structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It analyzes the rendered DOM and builds a skeleton that matches your existing layout.&lt;/p&gt;

&lt;p&gt;For 70–80% of real-world use cases (dashboards, cards, forms, tables), this removes the need to build skeleton components manually.&lt;/p&gt;

&lt;p&gt;Here’s what that looks like in practice:&lt;/p&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%2Fzq01czvq1w8dxirdav3g.gif" 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%2Fzq01czvq1w8dxirdav3g.gif" alt="AutoSkeleton Demo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Quick example
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;UserProfile component&lt;/code&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProfile&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex max-w-150 gap-4 p-5"&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;Image&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://avatars.githubusercontent.com/u/58282436?v=4"&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-20 w-20 rounded-full"&lt;/span&gt;
        &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Avatar"&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1"&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;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"m-0 mb-2 text-2xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Shanuka Hettiarachchi&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"m-0 mb-3 text-[#666]"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Senior Software Engineer at Wire Apps
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"cursor-pointer rounded-md border-none bg-blue-500 px-4 py-2 font-medium text-white"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Follow
        &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="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;&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;Now let's wrap it using &lt;code&gt;AutoSkeleton&lt;/code&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AutoSkeleton&lt;/span&gt;
   &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;config&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;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pulse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;baseColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#d4d4d8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&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="nc"&gt;UserProfile&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;AutoSkeleton&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;That's it.&lt;br&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%2Fxtikilirwxjn66ue407k.gif" 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%2Fxtikilirwxjn66ue407k.gif" alt=" " width="785" height="274"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  🚀 Features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Zero manual skeleton components&lt;/strong&gt;&lt;br&gt;
Wrap any React component — no separate &lt;code&gt;&amp;lt;ComponentSkeleton /&amp;gt;&lt;/code&gt; needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layout preservation&lt;/strong&gt;&lt;br&gt;
Maintains flexbox, grid, margins, padding, and gaps to prevent layout shift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-line text detection&lt;/strong&gt;&lt;br&gt;
Automatically infers text line count from element height.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table-aware rendering&lt;/strong&gt;&lt;br&gt;
Preserves semantic table structure (thead, tbody, tr, td) while skeletonizing content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Selective opt-out&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;data-no-skeleton&lt;/code&gt; or &lt;code&gt;.no-skeleton&lt;/code&gt; to keep specific elements visible during loading.&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;data-no-skeleton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Retry&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Smooth transitions
&lt;/h2&gt;

&lt;p&gt;Crossfades between skeleton and content for a natural UX.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurable
&lt;/h2&gt;

&lt;p&gt;Control animation, colors, border radius, and depth limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ When NOT to Use It
&lt;/h2&gt;

&lt;p&gt;AutoSkeleton uses DOM measurement, so it’s not ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely large or virtualized lists&lt;/li&gt;
&lt;li&gt;Highly dynamic layouts that constantly reflow&lt;/li&gt;
&lt;li&gt;Ultra SSR-sensitive pages where zero hydration shift is required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s designed primarily for dashboards, SaaS apps, admin panels, and structured UIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Exists
&lt;/h2&gt;

&lt;p&gt;The real pain isn’t "showing a loader".&lt;/p&gt;

&lt;p&gt;The real pain is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Keeping loading UI in sync with real UI without writing everything twice.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;AutoSkeleton tries to remove that duplication.&lt;/p&gt;

&lt;p&gt;If this sounds useful, give it a try:&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/ShanukJ/auto-skeleton" rel="noopener noreferrer"&gt;https://github.com/ShanukJ/auto-skeleton&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NPM: &lt;a href="https://www.npmjs.com/package/auto-skeleton-react" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/auto-skeleton-react&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docs: &lt;a href="https://autoskeleton.shanukj.me/docs" rel="noopener noreferrer"&gt;https://autoskeleton.shanukj.me/docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Demo: &lt;a href="https://autoskeleton.shanukj.me/" rel="noopener noreferrer"&gt;https://autoskeleton.shanukj.me/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d love to hear feedback — especially from production usage.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
