[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-75034":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":38,"readmeContent":39,"aiSummary":40,"trendingCount":16,"starSnapshotCount":16,"syncStatus":14,"lastSyncTime":41,"discoverSource":42},75034,"shimmer-from-structure","darula-hpp\u002Fshimmer-from-structure","darula-hpp","A structure-aware skeleton loader that mirrors your rendered UI at runtime. Zero layout duplication. Built for modern frameworks.","https:\u002F\u002Fshimmer-from-structure-docs.vercel.app",null,"TypeScript",1083,18,2,1,0,3,20,55.34,"MIT License",false,"main",true,[25,26,27,28,29,30,31,32,33,34,35,36,37],"angular","angularjs","developer-experience","loading-ui","react","shimmer","shimmer-effect","skeleton-loading","solidjs","svelte","sveltejs","vue","vuejs","2026-06-12 04:01:17","# Shimmer From Structure\n\nA structure-aware skeleton generator that mirrors your rendered UI at runtime. Automatically generates responsive shimmer states with zero layout duplication. Built for React, Vue, Angular, Svelte and SolidJS.\n\n**Documentation:** \u003Ca href=\"https:\u002F\u002Fshimmer-from-structure-docs.vercel.app\" target=\"_blank\" rel=\"noopener noreferrer\">Access Full Docs\u003C\u002Fa>\n\n![React](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FReact-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB)\n![Vue](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FVue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D)\n![Svelte](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSvelte-ff3e00?style=for-the-badge&logo=svelte&logoColor=white)\n![Angular](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FAngular-DD0031?style=for-the-badge&logo=angular&logoColor=white)\n![SolidJS](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSolidJS-%232c4f7c?style=for-the-badge&logo=solid&logoColor=white)\n\n![Shimmer From Structure Demo](https:\u002F\u002Fgithub.com\u002Fdarula-hpp\u002Fshimmer-from-structure\u002Fraw\u002Fmain\u002Fexample\u002Fpreview.gif)\n\n## Why This Library?\n\nTraditional shimmer libraries require you to:\n\n- Manually create skeleton components that mirror your real components\n- Maintain two versions of each component (real + skeleton)\n- Update skeletons every time your layout changes\n\n**Shimmer From Structure** eliminates all of that:\n\n- ✅ **Works with React, Vue, Svelte, Angular & SolidJS** - Simple, framework-specific adapters\n- ✅ Automatically measures your component's structure at runtime\n- ✅ Generates shimmer effects that match actual dimensions\n- ✅ Zero maintenance - works with any layout changes\n- ✅ Works with complex nested structures\n- ✅ Supports dynamic data with `templateProps`\n- ✅ Preserves container backgrounds during loading\n- ✅ Auto-detects border-radius from your CSS\n\n## Installation\n\n```bash\nnpm install shimmer-from-structure\n# or\nyarn add shimmer-from-structure\n# or\npnpm add shimmer-from-structure\n```\n\n## 🎯 Framework Support\n\nShimmer From Structure provides dedicated packages for **React and Vue**.\n\n### React\n\nReact support is built into the main package for backward compatibility:\n\n```javascript\n\u002F\u002F React projects (or @shimmer-from-structure\u002Freact)\nimport { Shimmer } from 'shimmer-from-structure';\n```\n\n### Vue 3\n\nVue support requires importing from the specific adapter:\n\n```javascript\n\u002F\u002F Vue 3 projects\nimport { Shimmer } from '@shimmer-from-structure\u002Fvue';\n```\n\n### Svelte\n\nSvelte support is provided via its own adapter:\n\n```javascript\n\u002F\u002F Svelte projects\nimport { Shimmer } from '@shimmer-from-structure\u002Fsvelte';\n```\n\n### Angular\n\nAngular support requires importing from the specific adapter:\n\n```typescript\n\u002F\u002F Angular projects\nimport { ShimmerComponent } from '@shimmer-from-structure\u002Fangular';\n```\n\n### SolidJS\n\nSolidJS support requires importing from the specific adapter:\n\n```tsx\n\u002F\u002F SolidJS projects\nimport { Shimmer } from '@shimmer-from-structure\u002Fsolid';\n```\n\n---\n\n# 📖 Basic Usage\n\n## React\n\n### Static Content\n\nFor components with hardcoded\u002Fstatic content:\n\n```tsx\nimport { Shimmer } from 'shimmer-from-structure';\n\nfunction UserCard() {\n  return (\n    \u003CShimmer loading={isLoading}>\n      \u003Cdiv className=\"card\">\n        \u003Cimg src=\"avatar.jpg\" className=\"avatar\" \u002F>\n        \u003Ch2>John Doe\u003C\u002Fh2>\n        \u003Cp>Software Engineer\u003C\u002Fp>\n      \u003C\u002Fdiv>\n    \u003C\u002FShimmer>\n  );\n}\n```\n\n## Vue\n\n### Static Content\n\n```vue\n\u003Cscript setup>\nimport { ref } from 'vue';\nimport { Shimmer } from '@shimmer-from-structure\u002Fvue';\n\nconst isLoading = ref(true);\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CShimmer :loading=\"isLoading\">\n    \u003Cdiv class=\"card\">\n      \u003Cimg src=\"avatar.jpg\" class=\"avatar\" \u002F>\n      \u003Ch2>John Doe\u003C\u002Fh2>\n      \u003Cp>Software Engineer\u003C\u002Fp>\n    \u003C\u002Fdiv>\n  \u003C\u002FShimmer>\n\u003C\u002Ftemplate>\n```\n\n## Svelte\n\n### Static Content\n\n```svelte\n\u003Cscript>\nimport { Shimmer } from '@shimmer-from-structure\u002Fsvelte';\n\nlet isLoading = $state(true);\n\u003C\u002Fscript>\n\n\u003CShimmer loading={isLoading}>\n  \u003Cdiv class=\"card\">\n    \u003Cimg src=\"avatar.jpg\" class=\"avatar\" \u002F>\n    \u003Ch2>John Doe\u003C\u002Fh2>\n    \u003Cp>Software Engineer\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002FShimmer>\n```\n\n## Angular\n\n### Static Content\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport { ShimmerComponent } from '@shimmer-from-structure\u002Fangular';\n\n@Component({\n  selector: 'app-user-card',\n  standalone: true,\n  imports: [ShimmerComponent],\n  template: `\n    \u003Cshimmer [loading]=\"isLoading()\">\n      \u003Cdiv class=\"card\">\n        \u003Cimg src=\"avatar.jpg\" class=\"avatar\" \u002F>\n        \u003Ch2>John Doe\u003C\u002Fh2>\n        \u003Cp>Software Engineer\u003C\u002Fp>\n      \u003C\u002Fdiv>\n    \u003C\u002Fshimmer>\n  `,\n})\nexport class UserCardComponent {\n  isLoading = signal(true);\n}\n```\n\n## SolidJS\n\n### Static Content\n\n```tsx\nimport { createSignal } from 'solid-js';\nimport { Shimmer } from '@shimmer-from-structure\u002Fsolid';\n\nfunction UserCard() {\n  const [isLoading, setIsLoading] = createSignal(true);\n\n  return (\n    \u003CShimmer loading={isLoading()}>\n      \u003Cdiv class=\"card\">\n        \u003Cimg src=\"avatar.jpg\" class=\"avatar\" \u002F>\n        \u003Ch2>John Doe\u003C\u002Fh2>\n        \u003Cp>Software Engineer\u003C\u002Fp>\n      \u003C\u002Fdiv>\n    \u003C\u002FShimmer>\n  );\n}\n```\n\n---\n\n### Dynamic Content with `templateProps`\n\nFor components that receive dynamic data via props, use `templateProps` to provide mock data for skeleton generation:\n\n**React**\n\n```tsx\nimport { Shimmer } from 'shimmer-from-structure';\n\n\u002F\u002F Your component that accepts props\nconst UserCard = ({ user }) => (\n  \u003Cdiv className=\"card\">\n    \u003Cimg src={user.avatar} className=\"avatar\" \u002F>\n    \u003Ch2>{user.name}\u003C\u002Fh2>\n    \u003Cp>{user.role}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n);\n\n\u002F\u002F Template data for the skeleton\nconst userTemplate = {\n  name: 'Loading...',\n  role: 'Loading role...',\n  avatar: 'placeholder.jpg',\n};\n\nfunction App() {\n  const [loading, setLoading] = useState(true);\n  const [user, setUser] = useState(null);\n\n  return (\n    \u003CShimmer loading={loading} templateProps={{ user: userTemplate }}>\n      \u003CUserCard user={user || userTemplate} \u002F>\n    \u003C\u002FShimmer>\n  );\n}\n```\n\n**Vue**\n\n```vue\n\u003Cscript setup>\nimport { ref } from 'vue';\nimport { Shimmer } from '@shimmer-from-structure\u002Fvue';\nimport UserCard from '.\u002FUserCard.vue';\n\nconst loading = ref(true);\nconst userTemplate = {\n  name: 'Loading...',\n  role: 'Loading role...',\n  avatar: 'placeholder.jpg',\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CShimmer :loading=\"loading\" :templateProps=\"{ user: userTemplate }\">\n    \u003CUserCard :user=\"user || userTemplate\" \u002F>\n  \u003C\u002FShimmer>\n\u003C\u002Ftemplate>\n```\n\n**Svelte**\n\n```svelte\n\u003Cscript>\nimport { Shimmer } from '@shimmer-from-structure\u002Fsvelte';\nimport UserCard from '.\u002FUserCard.svelte';\n\nlet { user } = $props();\nlet loading = $state(true);\n\nconst userTemplate = {\n  name: 'Loading...',\n  role: 'Loading role...',\n  avatar: 'placeholder.jpg',\n};\n\u003C\u002Fscript>\n\n\u003CShimmer loading={loading} templateProps={{ user: userTemplate }}>\n  \u003CUserCard user={user || userTemplate} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Angular**\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport { ShimmerComponent } from '@shimmer-from-structure\u002Fangular';\nimport { UserCardComponent } from '.\u002Fuser-card.component';\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [ShimmerComponent, UserCardComponent],\n  template: `\n    \u003Cshimmer [loading]=\"loading()\" [templateProps]=\"{ user: userTemplate }\">\n      \u003Capp-user-card [user]=\"user() || userTemplate\" \u002F>\n    \u003C\u002Fshimmer>\n  `,\n})\nexport class AppComponent {\n  loading = signal(true);\n  user = signal\u003CUser | null>(null);\n\n  userTemplate = {\n    name: 'Loading...',\n    role: 'Loading role...',\n    avatar: 'placeholder.jpg',\n  };\n}\n```\n\n**SolidJS**\n\n```tsx\nimport { createSignal } from 'solid-js';\nimport { Shimmer } from '@shimmer-from-structure\u002Fsolid';\nimport { UserCard } from '.\u002FUserCard';\n\nfunction App() {\n  const [loading, setLoading] = createSignal(true);\n  const [user, setUser] = createSignal(null);\n\n  const userTemplate = {\n    name: 'Loading...',\n    role: 'Loading role...',\n    avatar: 'placeholder.jpg',\n  };\n\n  return (\n    \u003CShimmer loading={loading()} templateProps={{ user: userTemplate }}>\n      \u003CUserCard user={user() || userTemplate} \u002F>\n    \u003C\u002FShimmer>\n  );\n}\n```\n\nThe `templateProps` object is spread onto the first child component when loading, allowing it to render with mock data for measurement.\n\n## 🎨 API Reference\n\n### `\u003CShimmer>` Props\n\n| Prop                   | Type                      | Default                    | Description                                               |\n| ---------------------- | ------------------------- | -------------------------- | --------------------------------------------------------- |\n| `loading`              | `boolean`                 | `true`                     | Whether to show shimmer effect or actual content          |\n| `children`             | `React.ReactNode`         | required                   | The content to render\u002Fmeasure                             |\n| `shimmerColor`         | `string`                  | `'rgba(255,255,255,0.15)'` | Color of the shimmer wave                                 |\n| `backgroundColor`      | `string`                  | `'rgba(255,255,255,0.08)'` | Background color of shimmer blocks                        |\n| `duration`             | `number`                  | `1.5`                      | Animation duration in seconds                             |\n| `fallbackBorderRadius` | `number`                  | `4`                        | Border radius (px) for elements with no CSS border-radius |\n| `templateProps`        | `Record\u003Cstring, unknown>` | -                          | Props to inject into first child for skeleton rendering   |\n\n### Example with All Props\n\n**React**\n\n```tsx\n\u003CShimmer\n  loading={isLoading}\n  shimmerColor=\"rgba(255, 255, 255, 0.2)\"\n  backgroundColor=\"rgba(255, 255, 255, 0.1)\"\n  duration={2}\n  fallbackBorderRadius={8}\n  templateProps={{\n    user: userTemplate,\n    settings: settingsTemplate,\n  }}\n>\n  \u003CMyComponent user={user} settings={settings} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Vue**\n\n```vue\n\u003CShimmer\n  :loading=\"isLoading\"\n  shimmerColor=\"rgba(255, 255, 255, 0.2)\"\n  backgroundColor=\"rgba(255, 255, 255, 0.1)\"\n  :duration=\"2\"\n  :fallbackBorderRadius=\"8\"\n  :templateProps=\"{\n    user: userTemplate,\n    settings: settingsTemplate,\n  }\"\n>\n  \u003CMyComponent :user=\"user\" :settings=\"settings\" \u002F>\n\u003C\u002FShimmer>\n```\n\n**Svelte**\n\n```svelte\n\u003CShimmer\n  loading={isLoading}\n  shimmerColor=\"rgba(255, 255, 255, 0.2)\"\n  backgroundColor=\"rgba(255, 255, 255, 0.1)\"\n  duration={2}\n  fallbackBorderRadius={8}\n  templateProps={{\n    user: userTemplate,\n    settings: settingsTemplate,\n  }}\n>\n  \u003CMyComponent {user} {settings} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Angular**\n\n```typescript\n\u003Cshimmer\n  [loading]=\"isLoading()\"\n  shimmerColor=\"rgba(255, 255, 255, 0.2)\"\n  backgroundColor=\"rgba(255, 255, 255, 0.1)\"\n  [duration]=\"2\"\n  [fallbackBorderRadius]=\"8\"\n  [templateProps]=\"{\n    user: userTemplate,\n    settings: settingsTemplate\n  }\">\n  \u003Capp-my-component\n    [user]=\"user()\"\n    [settings]=\"settings()\" \u002F>\n\u003C\u002Fshimmer>\n```\n\n**SolidJS**\n\n```tsx\n\u003CShimmer\n  loading={isLoading()}\n  shimmerColor=\"rgba(255, 255, 255, 0.2)\"\n  backgroundColor=\"rgba(255, 255, 255, 0.1)\"\n  duration={2}\n  fallbackBorderRadius={8}\n  templateProps={{\n    user: userTemplate,\n    settings: settingsTemplate,\n  }}\n>\n  \u003CMyComponent user={user()} settings={settings()} \u002F>\n\u003C\u002FShimmer>\n```\n\n## 🔧 How It Works\n\n1. **Visible Container Rendering**: When `loading={true}`, your component renders with transparent text but **visible container backgrounds**\n2. **Template Props Injection**: If `templateProps` is provided, it's spread onto the first child so dynamic components can render\n3. **DOM Measurement**: Uses `useLayoutEffect` to synchronously measure all leaf elements via `getBoundingClientRect()`\n4. **Border Radius Detection**: Automatically captures each element's computed `border-radius` from CSS\n5. **Shimmer Generation**: Creates absolutely-positioned shimmer blocks matching measured dimensions\n6. **Animation**: Applies smooth gradient animation that sweeps across each block\n\n### Key Features\n\n- **Container backgrounds visible**: Unlike `opacity: 0`, we use `color: transparent` so card backgrounds\u002Fborders show during loading\n- **Auto border-radius**: Circular avatars get circular shimmer blocks automatically\n- **Fallback radius**: Text elements (which have `border-radius: 0`) use `fallbackBorderRadius` to avoid sharp rectangles\n- **Dark-mode friendly**: Default colors use semi-transparent whites that work on any background\n\n## Examples\n\n### Dashboard with Multiple Sections\n\nEach section can have its own independent loading state:\n\n**React**\n\n```tsx\nfunction Dashboard() {\n  const [loadingUser, setLoadingUser] = useState(true);\n  const [loadingStats, setLoadingStats] = useState(true);\n\n  return (\n    \u003C>\n      {\u002F* User profile section *\u002F}\n      \u003CShimmer loading={loadingUser} templateProps={{ user: userTemplate }}>\n        \u003CUserProfile user={user} \u002F>\n      \u003C\u002FShimmer>\n\n      {\u002F* Stats section - with custom colors *\u002F}\n      \u003CShimmer\n        loading={loadingStats}\n        templateProps={{ stats: statsTemplate }}\n        shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n      >\n        \u003CStatsGrid stats={stats} \u002F>\n      \u003C\u002FShimmer>\n    \u003C\u002F>\n  );\n}\n```\n\n**Vue**\n\n```vue\n\u003Ctemplate>\n  \u003C!-- User profile section -->\n  \u003CShimmer :loading=\"loadingUser\" :templateProps=\"{ user: userTemplate }\">\n    \u003CUserProfile :user=\"user\" \u002F>\n  \u003C\u002FShimmer>\n\n  \u003C!-- Stats section - with custom colors -->\n  \u003CShimmer\n    :loading=\"loadingStats\"\n    :templateProps=\"{ stats: statsTemplate }\"\n    shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n  >\n    \u003CStatsGrid :stats=\"stats\" \u002F>\n  \u003C\u002FShimmer>\n\u003C\u002Ftemplate>\n```\n\n**Svelte**\n\n```svelte\n\u003CShimmer loading={loadingUser} templateProps={{ user: userTemplate }}>\n  \u003CUserProfile {user} \u002F>\n\u003C\u002FShimmer>\n\n\u003CShimmer\n  loading={loadingStats}\n  templateProps={{ stats: statsTemplate }}\n  shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n>\n  \u003CStatsGrid {stats} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Angular**\n\n```typescript\n@Component({\n  template: `\n    \u003C!-- User profile section -->\n    \u003Cshimmer [loading]=\"loadingUser()\" [templateProps]=\"{ user: userTemplate }\">\n      \u003Capp-user-profile [user]=\"user()\" \u002F>\n    \u003C\u002Fshimmer>\n\n    \u003C!-- Stats section - with custom colors -->\n    \u003Cshimmer\n      [loading]=\"loadingStats()\"\n      [templateProps]=\"{ stats: statsTemplate }\"\n      shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n    >\n      \u003Capp-stats-grid [stats]=\"stats()\" \u002F>\n    \u003C\u002Fshimmer>\n  `,\n})\nexport class DashboardComponent {\n  loadingUser = signal(true);\n  loadingStats = signal(true);\n  \u002F\u002F ...\n}\n```\n\n### Transactions List\n\n**React**\n\n```tsx\n\u003CShimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}>\n  \u003CTransactionsList transactions={transactions} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Vue**\n\n```vue\n\u003CShimmer :loading=\"loadingTransactions\" :templateProps=\"{ transactions: transactionsTemplate }\">\n  \u003CTransactionsList :transactions=\"transactions\" \u002F>\n\u003C\u002FShimmer>\n```\n\n**Svelte**\n\n```svelte\n\u003CShimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}>\n  \u003CTransactionsList {transactions} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Angular**\n\n```typescript\n\u003Cshimmer\n  [loading]=\"loadingTransactions()\"\n  [templateProps]=\"{ transactions: transactionsTemplate }\">\n  \u003Capp-transactions-list [transactions]=\"transactions()\" \u002F>\n\u003C\u002Fshimmer>\n```\n\n### Team Members Grid\n\n**React**\n\n```tsx\n\u003CShimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}>\n  \u003CTeamMembers members={team} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Vue**\n\n```vue\n\u003CShimmer :loading=\"loadingTeam\" :templateProps=\"{ members: teamTemplate }\">\n  \u003CTeamMembers :members=\"team\" \u002F>\n\u003C\u002FShimmer>\n```\n\n**Svelte**\n\n```svelte\n\u003CShimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}>\n  \u003CTeamMembers members={team} \u002F>\n\u003C\u002FShimmer>\n```\n\n**Angular**\n\n```typescript\n\u003Cshimmer\n  [loading]=\"loadingTeam()\"\n  [templateProps]=\"{ members: teamTemplate }\">\n  \u003Capp-team-members [members]=\"team()\" \u002F>\n\u003C\u002Fshimmer>\n```\n\n## 🔄 Using with React Suspense\n\nShimmer works seamlessly as a Suspense fallback. When used this way, `loading` is always `true` because React automatically unmounts the fallback and replaces it with the resolved component.\n\n### Basic Suspense Pattern\n\n```tsx\nimport { Suspense, lazy } from 'react';\nimport { Shimmer } from 'shimmer-from-structure';\n\nconst UserProfile = lazy(() => import('.\u002FUserProfile'));\n\nfunction App() {\n  return (\n    \u003CSuspense\n      fallback={\n        \u003CShimmer loading={true} templateProps={{ user: userTemplate }}>\n          \u003CUserProfile \u002F>\n        \u003C\u002FShimmer>\n      }\n    >\n      \u003CUserProfile userId=\"123\" \u002F>\n    \u003C\u002FSuspense>\n  );\n}\n```\n\n### Why `loading={true}` is Always Set\n\nWhen using Shimmer as a Suspense fallback:\n\n1. **Suspend**: React renders the fallback → Shimmer shows with `loading={true}`\n2. **Resolve**: React **replaces** the entire fallback with the real component\n3. The Shimmer is **unmounted**, not updated — so you never need to toggle `loading`\n\n### Performance Tips for Suspense\n\n**Memoize the fallback** to prevent re-renders:\n\n```tsx\nconst ShimmerFallback = React.memo(() => (\n  \u003CShimmer loading={true} templateProps={{ user: userTemplate }}>\n    \u003CUserProfile \u002F>\n  \u003C\u002FShimmer>\n));\n\n\u002F\u002F Usage\n\u003CSuspense fallback={\u003CShimmerFallback \u002F>}>\n  \u003CUserProfile userId=\"123\" \u002F>\n\u003C\u002FSuspense>;\n```\n\n**Keep templates lightweight** — the DOM is measured synchronously via `useLayoutEffect`, so avoid complex logic in your template.\n\n## Global Configuration\n\nYou can set default configuration for your entire app (or specific sections) using the context\u002Fprovider pattern. This is perfect for maintaining consistent themes without repeating props.\n\n### React (Context API)\n\n```tsx\nimport { Shimmer, ShimmerProvider } from '@shimmer-from-structure\u002Freact';\n\nfunction App() {\n  return (\n    \u002F\u002F Set global defaults\n    \u003CShimmerProvider\n      config={{\n        shimmerColor: 'rgba(56, 189, 248, 0.4)', \u002F\u002F Blue shimmer\n        backgroundColor: 'rgba(56, 189, 248, 0.1)', \u002F\u002F Blue background\n        duration: 2.5,\n        fallbackBorderRadius: 8,\n      }}\n    >\n      \u003CDashboard \u002F>\n    \u003C\u002FShimmerProvider>\n  );\n}\n```\n\n### Vue (Provide\u002FInject)\n\n```vue\n\u003C!-- App.vue -->\n\u003Cscript setup>\nimport { provideShimmerConfig } from '@shimmer-from-structure\u002Fvue';\n\nprovideShimmerConfig({\n  shimmerColor: 'rgba(56, 189, 248, 0.4)',\n  backgroundColor: 'rgba(56, 189, 248, 0.1)',\n  duration: 2.5,\n  fallbackBorderRadius: 8,\n});\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Crouter-view \u002F>\n\u003C\u002Ftemplate>\n```\n\n### Svelte (setShimmerConfig)\n\n```svelte\n\u003C!-- App.svelte or any parent component -->\n\u003Cscript>\nimport { setShimmerConfig } from '@shimmer-from-structure\u002Fsvelte';\nimport Dashboard from '.\u002FDashboard.svelte';\n\n\u002F\u002F Must be called at the top level during component initialization\nsetShimmerConfig({\n  shimmerColor: 'rgba(56, 189, 248, 0.4)',\n  backgroundColor: 'rgba(56, 189, 248, 0.1)',\n  duration: 2.5,\n  fallbackBorderRadius: 8,\n});\n\u003C\u002Fscript>\n\n\u003CDashboard \u002F>\n```\n\n### Angular (Dependency Injection)\n\n```typescript\n\u002F\u002F main.ts or bootstrapApplication\nimport { bootstrapApplication } from '@angular\u002Fplatform-browser';\nimport { provideShimmerConfig } from '@shimmer-from-structure\u002Fangular';\nimport { AppComponent } from '.\u002Fapp\u002Fapp.component';\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideShimmerConfig({\n      shimmerColor: 'rgba(56, 189, 248, 0.4)',\n      backgroundColor: 'rgba(56, 189, 248, 0.1)',\n      duration: 2.5,\n      fallbackBorderRadius: 8,\n    }),\n  ],\n});\n```\n\n### SolidJS (ShimmerProvider)\n\n```tsx\nimport { Shimmer, ShimmerProvider } from '@shimmer-from-structure\u002Fsolid';\n\nfunction App() {\n  return (\n    \u003CShimmerProvider\n      config={{\n        shimmerColor: 'rgba(56, 189, 248, 0.4)',\n        backgroundColor: 'rgba(56, 189, 248, 0.1)',\n        duration: 2.5,\n        fallbackBorderRadius: 8,\n      }}\n    >\n      \u003CDashboard \u002F>\n    \u003C\u002FShimmerProvider>\n  );\n}\n```\n\n---\n\nComponents inside the provider automatically inherit values. You can still override them locally:\n\n**React**\n\n```tsx\n\u002F\u002F Inherits blue theme from provider\n\u003CShimmer loading={true}>\u003CUserCard \u002F>\u003C\u002FShimmer>\n\n\u002F\u002F Overrides provider settings\n\u003CShimmer loading={true} duration={0.5}>\u003CFastCard \u002F>\u003C\u002FShimmer>\n```\n\n**Vue**\n\n```vue\n\u003C!-- Inherits blue theme from provider -->\n\u003CShimmer :loading=\"true\">\u003CUserCard \u002F>\u003C\u002FShimmer>\n\n\u003C!-- Overrides provider settings -->\n\u003CShimmer :loading=\"true\" :duration=\"0.5\">\u003CFastCard \u002F>\u003C\u002FShimmer>\n```\n\n**Svelte**\n\n```svelte\n\u003C!-- Inherits blue theme from provider -->\n\u003CShimmer loading={true}>\u003CUserCard \u002F>\u003C\u002FShimmer>\n\n\u003C!-- Overrides provider settings -->\n\u003CShimmer loading={true} duration={0.5}>\u003CFastCard \u002F>\u003C\u002FShimmer>\n```\n\n**Angular**\n\n```typescript\n\u003C!-- Inherits blue theme from injected config -->\n\u003Cshimmer [loading]=\"true\">\u003Capp-user-card \u002F>\u003C\u002Fshimmer>\n\n\u003C!-- Overrides injected settings -->\n\u003Cshimmer [loading]=\"true\" [duration]=\"0.5\">\u003Capp-fast-card \u002F>\u003C\u002Fshimmer>\n```\n\n**SolidJS**\n\n```tsx\n\u003C!-- Inherits blue theme from provider -->\n\u003CShimmer loading={true()}>\u003CUserCard \u002F>\u003C\u002FShimmer>\n\n\u003C!-- Overrides provider settings -->\n\u003CShimmer loading={true()} duration={0.5}>\u003CFastCard \u002F>\u003C\u002FShimmer>\n```\n\n### Accessing Config in Hooks\u002FComposables\n\nIf you need to access the current configuration in your own components:\n\n**React**\n\n```tsx\nimport { useShimmerConfig } from 'shimmer-from-structure';\n\nfunction MyComponent() {\n  const config = useShimmerConfig();\n  return \u003Cdiv style={{ background: config.backgroundColor }}>...\u003C\u002Fdiv>;\n}\n```\n\n**Vue**\n\n```javascript\nimport { useShimmerConfig } from '@shimmer-from-structure\u002Fvue';\n\nconst config = useShimmerConfig();\nconsole.log(config.value.backgroundColor);\n```\n\n**Svelte**\n\n```javascript\nimport { getShimmerConfig } from '@shimmer-from-structure\u002Fsvelte';\n\nconst config = getShimmerConfig();\nconsole.log(config.backgroundColor);\n```\n\n**Angular**\n\n```typescript\nimport { Component, inject } from '@angular\u002Fcore';\nimport { injectShimmerConfig } from '@shimmer-from-structure\u002Fangular';\n\n@Component({\n  selector: 'app-my-component',\n  template: `\u003Cdiv [style.background]=\"config.backgroundColor\">...\u003C\u002Fdiv>`,\n})\nexport class MyComponent {\n  config = injectShimmerConfig();\n}\n```\n\n**SolidJS**\n\n```tsx\nimport { useShimmerConfig } from '@shimmer-from-structure\u002Fsolid';\n\nfunction MyComponent() {\n  const config = useShimmerConfig();\n  return \u003Cdiv style={{ background: config.backgroundColor }}>...\u003C\u002Fdiv>;\n}\n```\n\n## Best Practices\n\n### 1. Use `templateProps` for Dynamic Data\n\nWhen your component receives data via props, always provide `templateProps` with mock data that matches the expected structure.\n\n### 2. Match Template Structure to Real Data\n\nEnsure your template data has the same array length and property structure as real data for accurate shimmer layout.\n\n### 3. Use Individual Shimmer Components\n\nWrap each section in its own Shimmer for independent loading states:\n\n```tsx\n\u002F\u002F ✅ Good - independent loading\n\u003CShimmer loading={loadingUsers}>\u003CUserList \u002F>\u003C\u002FShimmer>\n\u003CShimmer loading={loadingPosts}>\u003CPostList \u002F>\u003C\u002FShimmer>\n\n\u002F\u002F ❌ Avoid - all-or-nothing loading\n\u003CShimmer loading={loadingUsers || loadingPosts}>\n  \u003CUserList \u002F>\n  \u003CPostList \u002F>\n\u003C\u002FShimmer>\n```\n\n### 4. Consider Element Widths\n\nBlock elements like `\u003Ch1>`, `\u003Cp>` take full container width. If you want shimmer to match text width:\n\n```css\n.title {\n  width: fit-content;\n}\n```\n\n### 5. Provide Container Dimensions\n\nFor async components (like charts), ensure containers have explicit dimensions so shimmer has something to measure.\n\n## ⚡ Performance Considerations\n\n- Measurement happens only when `loading` changes to `true`\n- Uses `useLayoutEffect` for synchronous measurement (no flicker)\n- Minimal re-renders - only updates when loading state or children change\n- Lightweight DOM measurements using native browser APIs\n\n- Lightweight DOM measurements using native browser APIs\n\n## 🛠️ Development\n\nThis is a monorepo managed with npm workspaces. Each package can be built independently:\n\n```bash\n# Install dependencies\nnpm install\n\n# Build all packages\nnpm run build\n\n# Build individual packages\nnpm run build:core\nnpm run build:react\nnpm run build:vue\nnpm run build:svelte\nnpm run build:main\n\n# Run tests\nnpm test\n```\n\n## 📝 License\n\nMIT\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## 🐛 Known Limitations\n\n- **Async components**: Components that render asynchronously (like charts using `ResponsiveContainer`) may need explicit container dimensions\n- **Zero-dimension elements**: Elements with `display: none` or zero dimensions won't be captured\n- **SVG internals**: Only the outer `\u003Csvg>` element is captured, not internal paths\u002Fshapes\n\n## 🏗️ Monorepo Structure\n\nThis library is organized as a monorepo with four packages:\n\n| Package                           | Description                                 | Size     |\n| --------------------------------- | ------------------------------------------- | -------- |\n| `@shimmer-from-structure\u002Fcore`    | Framework-agnostic DOM utilities            | 1.44 kB  |\n| `@shimmer-from-structure\u002Freact`   | React adapter                               | 12.84 kB |\n| `@shimmer-from-structure\u002Fvue`     | Vue 3 adapter                               | 3.89 kB  |\n| `@shimmer-from-structure\u002Fsvelte`  | Svelte adapter                              | 4.60 kB  |\n| `@shimmer-from-structure\u002Fangular` | Angular adapter                             | 6.83 kB  |\n| `@shimmer-from-structure\u002Fsolid`   | SolidJS adapter                             | 4.01 kB  |\n| `shimmer-from-structure`          | Main package (React backward compatibility) | 0.93 kB  |\n\nThe core package contains all DOM measurement logic, while React, Vue, Svelte, Angular and SolidJS packages are thin wrappers that provide framework-specific APIs.\n\n## 🚧 Roadmap\n\n- [x] Dynamic data support via `templateProps`\n- [x] Auto border-radius detection\n- [x] Container background visibility\n- [x] **Vue.js adapter**\n- [x] **Svelte adapter**\n- [x] **Angular adapter**\n- [x] **SolidJS adapter**\n- [ ] Better async component support\n- [ ] Customizable shimmer direction (vertical, diagonal)\n- [ ] React Native support\n\n## 📚 Featured In\n\n- \u003Ca href=\"https:\u002F\u002Fneciudan.dev\u002Flets-build-dynamic-shimmer-skeletons\" target=\"_blank\" rel=\"noopener noreferrer\">Build your own shimmer skeleton that never goes out of sync\u003C\u002Fa> - Deep dive blog post on the implementation\n- \u003Ca href=\"https:\u002F\u002Fsvelte.dev\u002Fblog\u002Fwhats-new-in-svelte-march-2026\" target=\"_blank\" rel=\"noopener noreferrer\">What's new in Svelte: March 2026\u003C\u002Fa> - Featured in Svelte's official blog\n- \u003Ca href=\"https:\u002F\u002Freact.statuscode.com\u002Fissues\u002F459\" target=\"_blank\" rel=\"noopener noreferrer\">React Status Issue #459\u003C\u002Fa> - Featured in React Status newsletter\n\n---\n\nMade with ❤️ for developers tired of maintaining skeleton screens\n","Shimmer From Structure 是一个结构感知的骨架加载器，能够在运行时镜像渲染的UI，无需重复布局。它能够自动根据组件的实际结构生成响应式的闪烁效果，适用于React、Vue、Angular、Svelte和SolidJS等现代前端框架。该库的核心功能包括自动测量组件结构、匹配实际尺寸生成闪烁效果以及支持复杂嵌套结构和动态数据。特别适合需要提升用户体验且不想维护额外骨架组件的开发场景。通过简单的框架特定适配器，开发者可以轻松集成到项目中，从而实现零维护的高效加载动画。","2026-06-11 03:52:02","high_star"]