npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

add-skeleton

v1.5.0

Published

Zero-dependency CLI to inject customizable React + Tailwind skeleton component source code into your codebase

Readme

add-skeleton

npm version npm downloads license types node cli react tailwind

CLI to inject customizable React + Tailwind skeleton components into your codebase.

Zero runtime cost — generates source code you own, no dependencies shipped to your bundle.

  • 10 components — Skeleton, Card, Text, Avatar, TableRow, ListItem, Profile, Table, Grid, Comment
  • Smart detection — reads tsconfig aliases, framework, package manager automatically
  • RSC support — auto-adds "use client" directive for Next.js App Router
  • Theme system — centralized design tokens with runtime <SkeletonProvider> override
  • 5 CLI commands — add, init, update, remove, check
  • Interactive prompts — select components, target directory, preview with dry-run
  • Tailwind CSS 3+ — all styling via Tailwind utility classes
  • TypeScript — all components fully typed

Requirements

  • Node.js >= 18
  • React 18+
  • Tailwind CSS 3+

Quick Start

npx add-skeleton add

This will:

  • Detect your project structure (Next.js, Vite, TypeScript, etc.)
  • Read your tsconfig.json import aliases (e.g. @/*)
  • Detect Next.js App Router (RSC) and add "use client" directives
  • Prompt you to select which components to install
  • Write source files directly into src/components/ui/skeleton/

All Commands

# Interactive install (prompts for components and directory)
npx add-skeleton add

# Install everything with defaults
npx add-skeleton add --yes

# Preview without writing files
npx add-skeleton add --dry-run

# Overwrite existing files
npx add-skeleton add --overwrite

# Create a config file for repeated use
npx add-skeleton init

# Check project setup and dependencies
npx add-skeleton check

# Update installed components to latest version
npx add-skeleton update

# Remove skeleton components from project
npx add-skeleton remove

Smart Detection

Import Alias Resolution

The CLI reads your tsconfig.json compilerOptions.paths to detect import aliases:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "~/*": ["./src/*"]
    }
  }
}

The import hint after installation will use your detected alias:

Import components from: @/components/ui/skeleton

If no alias is detected, it falls back to a relative path.

Next.js App Router (RSC) Support

When a Next.js App Router project is detected (has app/ directory with layout.tsx), the CLI automatically prepends "use client"; to all generated files so they work in Server Components.

Framework Detection

Automatically detects: Next.js, Vite, Remix, Astro, Gatsby, CRA

Package Manager Detection

Detects: npm, pnpm, yarn, bun (from lockfiles)


Components

<Skeleton> — Base Primitive

The foundation component. All compositions use this internally.

import { Skeleton } from "@/components/ui/skeleton";

// Simple rectangle
<Skeleton />

// Circle avatar
<Skeleton variant="circle" className="h-10 w-10" />

// Text line
<Skeleton variant="text" className="h-4 w-3/4" />

// With count (multiple lines)
<Skeleton variant="text" count={4} />

// Custom color and no animation
<Skeleton
  variant="rounded"
  baseColor="bg-gray-100"
  animation={false}
  className="h-20 w-full"
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | variant | 'rect' \| 'circle' \| 'rounded' \| 'text' | 'rect' | Shape variant | | width | string \| number | — | CSS width | | height | string \| number | — | CSS height | | rounded | string | from theme | Border radius class | | animation | boolean | true | Toggle pulse animation | | baseColor | string | from theme | Background color class | | highlightColor | string | from theme | Shimmer highlight class | | count | number | 1 | Number of skeleton lines (supports decimals) | | gap | string | 'gap-2' | Gap between counted lines | | inline | boolean | false | Render counted lines inline | | className | string | '' | Tailwind class overrides | | ...props | HTMLDivAttributes | — | All div attributes pass through |


<SkeletonCard> — Card Layout

import { SkeletonCard } from "@/components/ui/skeleton";

// Default card with image + text
<SkeletonCard />

// Card with avatar and custom lines
<SkeletonCard withAvatar avatarSize="lg" lines={4} />

// Card without image
<SkeletonCard withImage={false} lines={2} />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | withImage | boolean | true | Show image block at top | | imageAspect | string | 'aspect-video' | Image aspect ratio | | withAvatar | boolean | false | Show avatar circle | | avatarSize | 'sm' \| 'md' \| 'lg' | 'md' | Avatar circle size | | lines | number | 3 | Number of text lines | | rounded | string | from theme | Card border radius | | gap | string | 'gap-3' | Gap between elements | | className | string | '' | Tailwind class overrides |


<SkeletonText> — Text Lines

import { SkeletonText } from "@/components/ui/skeleton";

// Default 3 lines
<SkeletonText />

// Custom lines with random widths
<SkeletonText lines={5} randomWidth />

// Short text with custom gap
<SkeletonText lines={2} lineHeight="h-3" lineGap="gap-1" lastLineWidth="w-1/3" />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | lines | number | 3 | Number of text lines | | lineHeight | string | 'h-3' | Height of each line | | lineGap | string | 'gap-2' | Gap between lines | | firstLineWidth | string | 'w-full' | Width of first line | | lastLineWidth | string | 'w-1/2' | Width of last line | | randomWidth | boolean | false | Randomize line widths | | className | string | '' | Tailwind class overrides |


<SkeletonAvatar> — Avatar Circle

import { SkeletonAvatar } from "@/components/ui/skeleton";

// Default avatar
<SkeletonAvatar />

// Large avatar with online status dot
<SkeletonAvatar size="xl" showStatus />

// Custom size
<SkeletonAvatar width="h-14 w-14" />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | size | 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | 'md' | Preset size | | width | string | — | Custom width (overrides size) | | height | string | — | Custom height (overrides size) | | showStatus | boolean | false | Show online status dot | | className | string | '' | Tailwind class overrides |


<SkeletonTableRow> — Table Row

import { SkeletonTableRow } from "@/components/ui/skeleton";

// Default 4-column row
<SkeletonTableRow />

// Row with avatar and 3 columns
<SkeletonTableRow withAvatar columns={3} />

// Row with image thumbnail
<SkeletonTableRow withImage columns={5} />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | columns | number | 4 | Number of columns | | columnWidths | string[] | auto | Custom width per column | | withAvatar | boolean | false | Show avatar circle | | withImage | boolean | false | Show image thumbnail | | animated | boolean | true | Toggle animation | | className | string | '' | Tailwind class overrides |


<SkeletonListItem> — List Item

import { SkeletonListItem } from "@/components/ui/skeleton";

// Default list item
<SkeletonListItem />

// Without avatar, with trailing button
<SkeletonListItem withAvatar={false} withTrailing lines={1} />

// Large avatar variant
<SkeletonListItem avatarSize="lg" lines={3} />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | withAvatar | boolean | true | Show avatar circle | | avatarSize | 'sm' \| 'md' \| 'lg' | 'md' | Avatar size | | lines | number | 2 | Number of text lines | | withTrailing | boolean | false | Show trailing element (button placeholder) | | className | string | '' | Tailwind class overrides |


<SkeletonProfile> — User Profile

import { SkeletonProfile } from "@/components/ui/skeleton";

// Default profile card
<SkeletonProfile />

// Centered with XL avatar
<SkeletonProfile centered avatarSize="xl" lines={4} />

// Without cover image
<SkeletonProfile withCoverImage={false} />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | avatarSize | 'md' \| 'lg' \| 'xl' | 'lg' | Avatar circle size | | withCoverImage | boolean | true | Show cover image banner | | coverAspect | string | 'aspect-[3/1]' | Cover image aspect ratio | | lines | number | 3 | Number of bio lines | | centered | boolean | false | Center align all content | | className | string | '' | Tailwind class overrides |


<SkeletonTable> — Full Table

import { SkeletonTable } from "@/components/ui/skeleton";

// Default 5-row, 4-column table
<SkeletonTable />

// Striped table with 8 rows
<SkeletonTable rows={8} striped />

// Table without header
<SkeletonTable header={false} columns={6} />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | rows | number | 5 | Number of data rows | | columns | number | 4 | Number of columns | | columnWidths | string[] | auto | Custom width per column | | header | boolean | true | Show header row | | striped | boolean | false | Alternate row shading | | className | string | '' | Tailwind class overrides |


<SkeletonGrid> — Responsive Grid

import { SkeletonGrid } from "@/components/ui/skeleton";

// Default card grid
<SkeletonGrid />

// Image grid with 9 items
<SkeletonGrid variant="image" items={9} columns={3} />

// Avatar grid
<SkeletonGrid variant="avatar" items={4} columns={2} />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | items | number | 6 | Number of grid items | | columns | number | 3 | Number of columns (responsive) | | variant | 'card' \| 'image' \| 'avatar' | 'card' | Grid item type | | aspect | string | 'aspect-square' | Aspect ratio (image variant) | | gap | string | 'gap-4' | Gap between items | | className | string | '' | Tailwind class overrides |


<SkeletonComment> — Comment Thread

import { SkeletonComment } from "@/components/ui/skeleton";

// Single comment
<SkeletonComment />

// Nested reply
<SkeletonComment nested lines={2} />

// Comment without action bar
<SkeletonComment showActions={false} />

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | withAvatar | boolean | true | Show avatar circle | | lines | number | 3 | Number of text lines | | nested | boolean | false | Indent as reply | | showActions | boolean | true | Show like/reply action bar | | className | string | '' | Tailwind class overrides |


Theme Customization

All components read from a centralized theme file. Edit theme.ts to change colors, animation speed, border radius, and sizes globally.

// theme.ts
export const skeletonTheme = {
  colors: {
    base: "bg-slate-200 dark:bg-slate-700",
    highlight: "bg-slate-300 dark:bg-slate-600",
    muted: "bg-slate-100 dark:bg-slate-800",
  },
  animation: {
    durations: { slow: "2s", normal: "1.5s", fast: "0.8s" },
    types: { pulse: "animate-pulse", shimmer: "skeleton-shimmer", wave: "skeleton-wave" },
  },
  radius: {
    none: "rounded-none", sm: "rounded-sm", md: "rounded-md",
    lg: "rounded-lg", xl: "rounded-xl", "2xl": "rounded-2xl", full: "rounded-full",
  },
  sizes: {
    xs: { height: "h-2", width: "w-16" },
    sm: { height: "h-3", width: "w-24" },
    md: { height: "h-4", width: "w-32" },
    lg: { height: "h-6", width: "w-48" },
    xl: { height: "h-8", width: "w-64" },
  },
  text: {
    lineHeight: "h-4",
    lineGap: "gap-2",
    firstLineWidth: "w-full",
    lastLineWidth: "w-1/2",
  },
};

Runtime Theme Override

Wrap components with <SkeletonProvider> to override theme at runtime:

import { SkeletonProvider, SkeletonCard } from "@/components/ui/skeleton";

function App() {
  return (
    <SkeletonProvider theme={{
      colors: { base: "bg-indigo-100 dark:bg-indigo-900" },
      animation: { durations: { normal: "1s" } },
    }}>
      <SkeletonCard />
    </SkeletonProvider>
  );
}

Configuration

Run npx add-skeleton init to create a skeleton.config.json:

{
  "path": "src/components/ui/skeleton",
  "animation": "pulse",
  "baseColor": "bg-slate-200 dark:bg-slate-700",
  "highlightColor": "bg-slate-300 dark:bg-slate-600",
  "radius": "md",
  "speed": "normal"
}

| Key | Type | Default | Description | |-----|------|---------|-------------| | path | string | 'src/components/ui/skeleton' | Install directory | | animation | 'pulse' \| 'shimmer' \| 'wave' | 'pulse' | Default animation | | baseColor | string | 'bg-slate-200 dark:bg-slate-700' | Background color | | highlightColor | string | 'bg-slate-300 dark:bg-slate-600' | Shimmer color | | radius | string | 'md' | Default border radius | | speed | 'slow' \| 'normal' \| 'fast' | 'normal' | Animation speed |


CLI Options

npx add-skeleton add [options]

Options:
  --cwd <path>    Working directory (default: current dir)
  --overwrite     Overwrite existing files
  --dry-run       Preview files without writing
  --yes           Skip prompts and use defaults
  --verbose       Show detailed output
  -h, --help      Display help

npx add-skeleton init [options]

Options:
  --cwd <path>    Working directory (default: current dir)
  --yes           Skip prompts and use defaults
  -h, --help      Display help

npx add-skeleton update [options]

Options:
  --cwd <path>    Working directory (default: current dir)
  --dry-run       Preview changes without writing
  --verbose       Show detailed output
  -h, --help      Display help

npx add-skeleton remove [options]

Options:
  --cwd <path>    Working directory (default: current dir)
  --yes           Skip confirmation prompt
  -h, --help      Display help

npx add-skeleton check [options]

Options:
  --cwd <path>    Working directory (default: current dir)
  --verbose       Show detailed output
  -h, --help      Display help

File Structure

After installation, your project will contain:

src/components/ui/skeleton/
├── index.ts          # Barrel exports
├── theme.ts          # Design tokens and provider
├── primitives.tsx    # Base <Skeleton /> component
├── card.tsx          # SkeletonCard
├── text.tsx          # SkeletonText
├── avatar.tsx        # SkeletonAvatar
├── table-row.tsx     # SkeletonTableRow
├── list-item.tsx     # SkeletonListItem
├── profile.tsx       # SkeletonProfile
├── table.tsx         # SkeletonTable
├── grid.tsx          # SkeletonGrid
└── comment.tsx       # SkeletonComment

License

MIT