{"id":41662095,"url":"https://github.com/darula-hpp/shimmer-from-structure","last_synced_at":"2026-04-11T14:07:13.793Z","repository":{"id":333741269,"uuid":"1138504933","full_name":"darula-hpp/shimmer-from-structure","owner":"darula-hpp","description":"A structure-aware skeleton loader that mirrors your rendered UI at runtime. Zero layout duplication. Built for modern frameworks.","archived":false,"fork":false,"pushed_at":"2026-02-21T08:19:42.000Z","size":6635,"stargazers_count":743,"open_issues_count":0,"forks_count":14,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-21T16:08:47.948Z","etag":null,"topics":["angular","angularjs","developer-experience","loading-ui","react","shimmer","shimmer-effect","skeleton-loading","solidjs","svelte","sveltejs","vue","vuejs"],"latest_commit_sha":null,"homepage":"https://shimmer-from-structure-docs.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/darula-hpp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-20T18:59:27.000Z","updated_at":"2026-02-21T11:03:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/darula-hpp/shimmer-from-structure","commit_stats":null,"previous_names":["darula-hpp/shimmer-from-structure"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/darula-hpp/shimmer-from-structure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darula-hpp%2Fshimmer-from-structure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darula-hpp%2Fshimmer-from-structure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darula-hpp%2Fshimmer-from-structure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darula-hpp%2Fshimmer-from-structure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darula-hpp","download_url":"https://codeload.github.com/darula-hpp/shimmer-from-structure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darula-hpp%2Fshimmer-from-structure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30056056,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T18:21:05.932Z","status":"ssl_error","status_checked_at":"2026-03-03T18:20:59.341Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angularjs","developer-experience","loading-ui","react","shimmer","shimmer-effect","skeleton-loading","solidjs","svelte","sveltejs","vue","vuejs"],"created_at":"2026-01-24T17:09:52.197Z","updated_at":"2026-04-11T14:07:13.781Z","avatar_url":"https://github.com/darula-hpp.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Third Party Components"],"sub_categories":["Loaders"],"readme":"# 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:** [Access Full Docs](https://shimmer-from-structure-docs.vercel.app)\n\n![React](https://img.shields.io/badge/React-%2320232a.svg?style=for-the-badge\u0026logo=react\u0026logoColor=%2361DAFB)\n![Vue](https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge\u0026logo=vuedotjs\u0026logoColor=4FC08D)\n![Svelte](https://img.shields.io/badge/Svelte-ff3e00?style=for-the-badge\u0026logo=svelte\u0026logoColor=white)\n![Angular](https://img.shields.io/badge/Angular-DD0031?style=for-the-badge\u0026logo=angular\u0026logoColor=white)\n![SolidJS](https://img.shields.io/badge/SolidJS-%232c4f7c?style=for-the-badge\u0026logo=solid\u0026logoColor=white)\n\n![Shimmer From Structure Demo](https://github.com/darula-hpp/shimmer-from-structure/raw/main/example/preview.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 \u0026 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// React projects (or @shimmer-from-structure/react)\nimport { Shimmer } from 'shimmer-from-structure';\n```\n\n### Vue 3\n\nVue support requires importing from the specific adapter:\n\n```javascript\n// Vue 3 projects\nimport { Shimmer } from '@shimmer-from-structure/vue';\n```\n\n### Svelte\n\nSvelte support is provided via its own adapter:\n\n```javascript\n// Svelte projects\nimport { Shimmer } from '@shimmer-from-structure/svelte';\n```\n\n### Angular\n\nAngular support requires importing from the specific adapter:\n\n```typescript\n// Angular projects\nimport { ShimmerComponent } from '@shimmer-from-structure/angular';\n```\n\n### SolidJS\n\nSolidJS support requires importing from the specific adapter:\n\n```tsx\n// SolidJS projects\nimport { Shimmer } from '@shimmer-from-structure/solid';\n```\n\n---\n\n# 📖 Basic Usage\n\n## React\n\n### Static Content\n\nFor components with hardcoded/static content:\n\n```tsx\nimport { Shimmer } from 'shimmer-from-structure';\n\nfunction UserCard() {\n  return (\n    \u003cShimmer loading={isLoading}\u003e\n      \u003cdiv className=\"card\"\u003e\n        \u003cimg src=\"avatar.jpg\" className=\"avatar\" /\u003e\n        \u003ch2\u003eJohn Doe\u003c/h2\u003e\n        \u003cp\u003eSoftware Engineer\u003c/p\u003e\n      \u003c/div\u003e\n    \u003c/Shimmer\u003e\n  );\n}\n```\n\n## Vue\n\n### Static Content\n\n```vue\n\u003cscript setup\u003e\nimport { ref } from 'vue';\nimport { Shimmer } from '@shimmer-from-structure/vue';\n\nconst isLoading = ref(true);\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cShimmer :loading=\"isLoading\"\u003e\n    \u003cdiv class=\"card\"\u003e\n      \u003cimg src=\"avatar.jpg\" class=\"avatar\" /\u003e\n      \u003ch2\u003eJohn Doe\u003c/h2\u003e\n      \u003cp\u003eSoftware Engineer\u003c/p\u003e\n    \u003c/div\u003e\n  \u003c/Shimmer\u003e\n\u003c/template\u003e\n```\n\n## Svelte\n\n### Static Content\n\n```svelte\n\u003cscript\u003e\nimport { Shimmer } from '@shimmer-from-structure/svelte';\n\nlet isLoading = $state(true);\n\u003c/script\u003e\n\n\u003cShimmer loading={isLoading}\u003e\n  \u003cdiv class=\"card\"\u003e\n    \u003cimg src=\"avatar.jpg\" class=\"avatar\" /\u003e\n    \u003ch2\u003eJohn Doe\u003c/h2\u003e\n    \u003cp\u003eSoftware Engineer\u003c/p\u003e\n  \u003c/div\u003e\n\u003c/Shimmer\u003e\n```\n\n## Angular\n\n### Static Content\n\n```typescript\nimport { Component, signal } from '@angular/core';\nimport { ShimmerComponent } from '@shimmer-from-structure/angular';\n\n@Component({\n  selector: 'app-user-card',\n  standalone: true,\n  imports: [ShimmerComponent],\n  template: `\n    \u003cshimmer [loading]=\"isLoading()\"\u003e\n      \u003cdiv class=\"card\"\u003e\n        \u003cimg src=\"avatar.jpg\" class=\"avatar\" /\u003e\n        \u003ch2\u003eJohn Doe\u003c/h2\u003e\n        \u003cp\u003eSoftware Engineer\u003c/p\u003e\n      \u003c/div\u003e\n    \u003c/shimmer\u003e\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/solid';\n\nfunction UserCard() {\n  const [isLoading, setIsLoading] = createSignal(true);\n\n  return (\n    \u003cShimmer loading={isLoading()}\u003e\n      \u003cdiv class=\"card\"\u003e\n        \u003cimg src=\"avatar.jpg\" class=\"avatar\" /\u003e\n        \u003ch2\u003eJohn Doe\u003c/h2\u003e\n        \u003cp\u003eSoftware Engineer\u003c/p\u003e\n      \u003c/div\u003e\n    \u003c/Shimmer\u003e\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// Your component that accepts props\nconst UserCard = ({ user }) =\u003e (\n  \u003cdiv className=\"card\"\u003e\n    \u003cimg src={user.avatar} className=\"avatar\" /\u003e\n    \u003ch2\u003e{user.name}\u003c/h2\u003e\n    \u003cp\u003e{user.role}\u003c/p\u003e\n  \u003c/div\u003e\n);\n\n// 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 }}\u003e\n      \u003cUserCard user={user || userTemplate} /\u003e\n    \u003c/Shimmer\u003e\n  );\n}\n```\n\n**Vue**\n\n```vue\n\u003cscript setup\u003e\nimport { ref } from 'vue';\nimport { Shimmer } from '@shimmer-from-structure/vue';\nimport UserCard from './UserCard.vue';\n\nconst loading = ref(true);\nconst userTemplate = {\n  name: 'Loading...',\n  role: 'Loading role...',\n  avatar: 'placeholder.jpg',\n};\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cShimmer :loading=\"loading\" :templateProps=\"{ user: userTemplate }\"\u003e\n    \u003cUserCard :user=\"user || userTemplate\" /\u003e\n  \u003c/Shimmer\u003e\n\u003c/template\u003e\n```\n\n**Svelte**\n\n```svelte\n\u003cscript\u003e\nimport { Shimmer } from '@shimmer-from-structure/svelte';\nimport UserCard from './UserCard.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/script\u003e\n\n\u003cShimmer loading={loading} templateProps={{ user: userTemplate }}\u003e\n  \u003cUserCard user={user || userTemplate} /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Angular**\n\n```typescript\nimport { Component, signal } from '@angular/core';\nimport { ShimmerComponent } from '@shimmer-from-structure/angular';\nimport { UserCardComponent } from './user-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 }\"\u003e\n      \u003capp-user-card [user]=\"user() || userTemplate\" /\u003e\n    \u003c/shimmer\u003e\n  `,\n})\nexport class AppComponent {\n  loading = signal(true);\n  user = signal\u003cUser | null\u003e(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/solid';\nimport { UserCard } from './UserCard';\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 }}\u003e\n      \u003cUserCard user={user() || userTemplate} /\u003e\n    \u003c/Shimmer\u003e\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\u003e` 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/measure                             |\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\u003e` | -                          | 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\u003e\n  \u003cMyComponent user={user} settings={settings} /\u003e\n\u003c/Shimmer\u003e\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\u003e\n  \u003cMyComponent :user=\"user\" :settings=\"settings\" /\u003e\n\u003c/Shimmer\u003e\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\u003e\n  \u003cMyComponent {user} {settings} /\u003e\n\u003c/Shimmer\u003e\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  }\"\u003e\n  \u003capp-my-component\n    [user]=\"user()\"\n    [settings]=\"settings()\" /\u003e\n\u003c/shimmer\u003e\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\u003e\n  \u003cMyComponent user={user()} settings={settings()} /\u003e\n\u003c/Shimmer\u003e\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/borders 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\u003e\n      {/* User profile section */}\n      \u003cShimmer loading={loadingUser} templateProps={{ user: userTemplate }}\u003e\n        \u003cUserProfile user={user} /\u003e\n      \u003c/Shimmer\u003e\n\n      {/* Stats section - with custom colors */}\n      \u003cShimmer\n        loading={loadingStats}\n        templateProps={{ stats: statsTemplate }}\n        shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n      \u003e\n        \u003cStatsGrid stats={stats} /\u003e\n      \u003c/Shimmer\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n**Vue**\n\n```vue\n\u003ctemplate\u003e\n  \u003c!-- User profile section --\u003e\n  \u003cShimmer :loading=\"loadingUser\" :templateProps=\"{ user: userTemplate }\"\u003e\n    \u003cUserProfile :user=\"user\" /\u003e\n  \u003c/Shimmer\u003e\n\n  \u003c!-- Stats section - with custom colors --\u003e\n  \u003cShimmer\n    :loading=\"loadingStats\"\n    :templateProps=\"{ stats: statsTemplate }\"\n    shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n  \u003e\n    \u003cStatsGrid :stats=\"stats\" /\u003e\n  \u003c/Shimmer\u003e\n\u003c/template\u003e\n```\n\n**Svelte**\n\n```svelte\n\u003cShimmer loading={loadingUser} templateProps={{ user: userTemplate }}\u003e\n  \u003cUserProfile {user} /\u003e\n\u003c/Shimmer\u003e\n\n\u003cShimmer\n  loading={loadingStats}\n  templateProps={{ stats: statsTemplate }}\n  shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n\u003e\n  \u003cStatsGrid {stats} /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Angular**\n\n```typescript\n@Component({\n  template: `\n    \u003c!-- User profile section --\u003e\n    \u003cshimmer [loading]=\"loadingUser()\" [templateProps]=\"{ user: userTemplate }\"\u003e\n      \u003capp-user-profile [user]=\"user()\" /\u003e\n    \u003c/shimmer\u003e\n\n    \u003c!-- Stats section - with custom colors --\u003e\n    \u003cshimmer\n      [loading]=\"loadingStats()\"\n      [templateProps]=\"{ stats: statsTemplate }\"\n      shimmerColor=\"rgba(20, 184, 166, 0.2)\"\n    \u003e\n      \u003capp-stats-grid [stats]=\"stats()\" /\u003e\n    \u003c/shimmer\u003e\n  `,\n})\nexport class DashboardComponent {\n  loadingUser = signal(true);\n  loadingStats = signal(true);\n  // ...\n}\n```\n\n### Transactions List\n\n**React**\n\n```tsx\n\u003cShimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}\u003e\n  \u003cTransactionsList transactions={transactions} /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Vue**\n\n```vue\n\u003cShimmer :loading=\"loadingTransactions\" :templateProps=\"{ transactions: transactionsTemplate }\"\u003e\n  \u003cTransactionsList :transactions=\"transactions\" /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Svelte**\n\n```svelte\n\u003cShimmer loading={loadingTransactions} templateProps={{ transactions: transactionsTemplate }}\u003e\n  \u003cTransactionsList {transactions} /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Angular**\n\n```typescript\n\u003cshimmer\n  [loading]=\"loadingTransactions()\"\n  [templateProps]=\"{ transactions: transactionsTemplate }\"\u003e\n  \u003capp-transactions-list [transactions]=\"transactions()\" /\u003e\n\u003c/shimmer\u003e\n```\n\n### Team Members Grid\n\n**React**\n\n```tsx\n\u003cShimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}\u003e\n  \u003cTeamMembers members={team} /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Vue**\n\n```vue\n\u003cShimmer :loading=\"loadingTeam\" :templateProps=\"{ members: teamTemplate }\"\u003e\n  \u003cTeamMembers :members=\"team\" /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Svelte**\n\n```svelte\n\u003cShimmer loading={loadingTeam} templateProps={{ members: teamTemplate }}\u003e\n  \u003cTeamMembers members={team} /\u003e\n\u003c/Shimmer\u003e\n```\n\n**Angular**\n\n```typescript\n\u003cshimmer\n  [loading]=\"loadingTeam()\"\n  [templateProps]=\"{ members: teamTemplate }\"\u003e\n  \u003capp-team-members [members]=\"team()\" /\u003e\n\u003c/shimmer\u003e\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(() =\u003e import('./UserProfile'));\n\nfunction App() {\n  return (\n    \u003cSuspense\n      fallback={\n        \u003cShimmer loading={true} templateProps={{ user: userTemplate }}\u003e\n          \u003cUserProfile /\u003e\n        \u003c/Shimmer\u003e\n      }\n    \u003e\n      \u003cUserProfile userId=\"123\" /\u003e\n    \u003c/Suspense\u003e\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(() =\u003e (\n  \u003cShimmer loading={true} templateProps={{ user: userTemplate }}\u003e\n    \u003cUserProfile /\u003e\n  \u003c/Shimmer\u003e\n));\n\n// Usage\n\u003cSuspense fallback={\u003cShimmerFallback /\u003e}\u003e\n  \u003cUserProfile userId=\"123\" /\u003e\n\u003c/Suspense\u003e;\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/provider 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/react';\n\nfunction App() {\n  return (\n    // Set global defaults\n    \u003cShimmerProvider\n      config={{\n        shimmerColor: 'rgba(56, 189, 248, 0.4)', // Blue shimmer\n        backgroundColor: 'rgba(56, 189, 248, 0.1)', // Blue background\n        duration: 2.5,\n        fallbackBorderRadius: 8,\n      }}\n    \u003e\n      \u003cDashboard /\u003e\n    \u003c/ShimmerProvider\u003e\n  );\n}\n```\n\n### Vue (Provide/Inject)\n\n```vue\n\u003c!-- App.vue --\u003e\n\u003cscript setup\u003e\nimport { provideShimmerConfig } from '@shimmer-from-structure/vue';\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/script\u003e\n\n\u003ctemplate\u003e\n  \u003crouter-view /\u003e\n\u003c/template\u003e\n```\n\n### Svelte (setShimmerConfig)\n\n```svelte\n\u003c!-- App.svelte or any parent component --\u003e\n\u003cscript\u003e\nimport { setShimmerConfig } from '@shimmer-from-structure/svelte';\nimport Dashboard from './Dashboard.svelte';\n\n// 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/script\u003e\n\n\u003cDashboard /\u003e\n```\n\n### Angular (Dependency Injection)\n\n```typescript\n// main.ts or bootstrapApplication\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { provideShimmerConfig } from '@shimmer-from-structure/angular';\nimport { AppComponent } from './app/app.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/solid';\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    \u003e\n      \u003cDashboard /\u003e\n    \u003c/ShimmerProvider\u003e\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// Inherits blue theme from provider\n\u003cShimmer loading={true}\u003e\u003cUserCard /\u003e\u003c/Shimmer\u003e\n\n// Overrides provider settings\n\u003cShimmer loading={true} duration={0.5}\u003e\u003cFastCard /\u003e\u003c/Shimmer\u003e\n```\n\n**Vue**\n\n```vue\n\u003c!-- Inherits blue theme from provider --\u003e\n\u003cShimmer :loading=\"true\"\u003e\u003cUserCard /\u003e\u003c/Shimmer\u003e\n\n\u003c!-- Overrides provider settings --\u003e\n\u003cShimmer :loading=\"true\" :duration=\"0.5\"\u003e\u003cFastCard /\u003e\u003c/Shimmer\u003e\n```\n\n**Svelte**\n\n```svelte\n\u003c!-- Inherits blue theme from provider --\u003e\n\u003cShimmer loading={true}\u003e\u003cUserCard /\u003e\u003c/Shimmer\u003e\n\n\u003c!-- Overrides provider settings --\u003e\n\u003cShimmer loading={true} duration={0.5}\u003e\u003cFastCard /\u003e\u003c/Shimmer\u003e\n```\n\n**Angular**\n\n```typescript\n\u003c!-- Inherits blue theme from injected config --\u003e\n\u003cshimmer [loading]=\"true\"\u003e\u003capp-user-card /\u003e\u003c/shimmer\u003e\n\n\u003c!-- Overrides injected settings --\u003e\n\u003cshimmer [loading]=\"true\" [duration]=\"0.5\"\u003e\u003capp-fast-card /\u003e\u003c/shimmer\u003e\n```\n\n**SolidJS**\n\n```tsx\n\u003c!-- Inherits blue theme from provider --\u003e\n\u003cShimmer loading={true()}\u003e\u003cUserCard /\u003e\u003c/Shimmer\u003e\n\n\u003c!-- Overrides provider settings --\u003e\n\u003cShimmer loading={true()} duration={0.5}\u003e\u003cFastCard /\u003e\u003c/Shimmer\u003e\n```\n\n### Accessing Config in Hooks/Composables\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 }}\u003e...\u003c/div\u003e;\n}\n```\n\n**Vue**\n\n```javascript\nimport { useShimmerConfig } from '@shimmer-from-structure/vue';\n\nconst config = useShimmerConfig();\nconsole.log(config.value.backgroundColor);\n```\n\n**Svelte**\n\n```javascript\nimport { getShimmerConfig } from '@shimmer-from-structure/svelte';\n\nconst config = getShimmerConfig();\nconsole.log(config.backgroundColor);\n```\n\n**Angular**\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { injectShimmerConfig } from '@shimmer-from-structure/angular';\n\n@Component({\n  selector: 'app-my-component',\n  template: `\u003cdiv [style.background]=\"config.backgroundColor\"\u003e...\u003c/div\u003e`,\n})\nexport class MyComponent {\n  config = injectShimmerConfig();\n}\n```\n\n**SolidJS**\n\n```tsx\nimport { useShimmerConfig } from '@shimmer-from-structure/solid';\n\nfunction MyComponent() {\n  const config = useShimmerConfig();\n  return \u003cdiv style={{ background: config.backgroundColor }}\u003e...\u003c/div\u003e;\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// ✅ Good - independent loading\n\u003cShimmer loading={loadingUsers}\u003e\u003cUserList /\u003e\u003c/Shimmer\u003e\n\u003cShimmer loading={loadingPosts}\u003e\u003cPostList /\u003e\u003c/Shimmer\u003e\n\n// ❌ Avoid - all-or-nothing loading\n\u003cShimmer loading={loadingUsers || loadingPosts}\u003e\n  \u003cUserList /\u003e\n  \u003cPostList /\u003e\n\u003c/Shimmer\u003e\n```\n\n### 4. Consider Element Widths\n\nBlock elements like `\u003ch1\u003e`, `\u003cp\u003e` 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\u003e` element is captured, not internal paths/shapes\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/core`    | Framework-agnostic DOM utilities            | 1.44 kB  |\n| `@shimmer-from-structure/react`   | React adapter                               | 12.84 kB |\n| `@shimmer-from-structure/vue`     | Vue 3 adapter                               | 3.89 kB  |\n| `@shimmer-from-structure/svelte`  | Svelte adapter                              | 4.60 kB  |\n| `@shimmer-from-structure/angular` | Angular adapter                             | 6.83 kB  |\n| `@shimmer-from-structure/solid`   | 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://neciudan.dev/lets-build-dynamic-shimmer-skeletons\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eBuild your own shimmer skeleton that never goes out of sync\u003c/a\u003e - Deep dive blog post on the implementation\n- \u003ca href=\"https://svelte.dev/blog/whats-new-in-svelte-march-2026\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWhat's new in Svelte: March 2026\u003c/a\u003e - Featured in Svelte's official blog\n- \u003ca href=\"https://react.statuscode.com/issues/459\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eReact Status Issue #459\u003c/a\u003e - Featured in React Status newsletter\n\n---\n\nMade with ❤️ for developers tired of maintaining skeleton screens\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarula-hpp%2Fshimmer-from-structure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarula-hpp%2Fshimmer-from-structure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarula-hpp%2Fshimmer-from-structure/lists"}