A TypeScript-based CLI tool that statically analyzes React/Next.js projects using AST parsing to automatically generate skeleton (loading) placeholder components that mirror the original UI's layout and styling.
- 🔍 Static Analysis - Uses AST parsing to analyze React/Next.js components
- 🎨 Layout Preservation - Maintains original component structure (flex, grid, padding, margin, etc.)
- ⚡ Data-Fetch Detection - Identifies components with data fetching patterns
- 🎭 Multiple Styling Systems - Supports Tailwind CSS, CSS Modules, Styled Components, Emotion
- 🔌 Plugin Architecture - Extensible styling adapter system
- 📦 Zero Runtime Dependencies - Generated skeletons are pure React components
# Clone the repository
git clone https://github.com/minaa66/skelton-auto.git
cd skelton-auto
# Install dependencies
npm install
# Build all packages
npm run build
# Link the CLI globally
npm link# Basic usage - scan and generate skeleton files
skelton-auto ./my-next-app
# Preview changes without writing files
skelton-auto ./my-next-app --dry-run
# Specify styling system
skelton-auto ./my-next-app --style tailwind
# Generate AND integrate into original components
skelton-auto ./my-next-app --integrate
# Custom output directory
skelton-auto ./my-next-app --output ./src/skeletonsAnalyze a project and generate skeleton components.
Options:
| Option | Alias | Default | Description |
|---|---|---|---|
--style |
-s |
tailwind |
Styling system (tailwind, css-modules, styled-components, emotion) |
--output |
-o |
(same as source) | Output directory for skeleton files |
--integrate |
-i |
false |
Inject skeleton usage into original components |
--config |
-c |
skelton.config.js |
Path to config file |
--include |
**/*.tsx,**/*.jsx |
Glob patterns for files to include | |
--exclude |
node_modules/** |
Glob patterns for files to exclude | |
--dry-run |
false |
Preview changes without writing files | |
--init |
false |
Create a default config file |
Create a default configuration file.
Analyze components without generating skeletons.
Create a skelton.config.js file in your project root:
// skelton.config.js
module.exports = {
// Styling system to use
style: 'tailwind',
// Output directory for skeleton files (optional)
outputDir: './src/skeletons',
// Whether to inject skeleton usage into original components
integrate: false,
// Glob patterns for files to include
include: ['src/**/*.tsx', 'src/**/*.jsx'],
// Glob patterns for files to exclude
exclude: [
'**/*.test.*',
'**/*.stories.*',
'**/Skeleton.tsx',
'node_modules/**'
],
// Suffix for generated skeleton component names
skeletonSuffix: 'Skeleton',
// Animation class for placeholders (Tailwind)
animationClass: 'animate-pulse'
};The tool scans your project for React component files (.tsx, .jsx) using fast-glob, respecting include/exclude patterns.
Each file is parsed into an Abstract Syntax Tree (AST) using @babel/parser, supporting TypeScript and JSX syntax.
The analyzer:
- Identifies React component declarations (function declarations, arrow functions)
- Detects data-fetching patterns:
useEffectuseSWRuseQuery(React Query/Apollo)fetchaxiosgetServerSidePropsgetStaticProps
- Extracts the visual structure from JSX return statements
- Captures props and their types
For each candidate component:
- Preserves layout classes (flex, grid, padding, margin, width, height)
- Replaces text nodes with animated placeholder divs
- Replaces images with placeholder boxes
- Maintains the original component hierarchy
Generates skeleton component files:
- Named exports for easy imports
- TypeScript compatible
- Ready to use in your application
// src/components/UserCard.tsx
import { useEffect, useState } from 'react'
export default function UserCard({ userId }: { userId: string }) {
const [user, setUser] = useState(null)
useEffect(() => {
fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser)
}, [userId])
return (
<div className="flex items-center gap-4 p-4 rounded-lg shadow">
<img className="w-12 h-12 rounded-full" src={user?.avatar} alt={user?.name} />
<div className="flex flex-col gap-1">
<span className="text-lg font-semibold">{user?.name}</span>
<span className="text-sm text-gray-500">{user?.email}</span>
</div>
</div>
)
}// src/skeletons/UserCardSkeleton.tsx (auto-generated)
import React from 'react';
export const UserCardSkeleton: React.FC = () => {
return (
<div className="flex items-center gap-4 p-4 rounded-lg shadow animate-pulse">
<div className="w-12 h-12 rounded-full bg-gray-200">
{/* Image placeholder */}
</div>
<div className="flex flex-col gap-1">
<div className="bg-gray-200 rounded animate-pulse">
{/* Text placeholder */}
</div>
<div className="bg-gray-200 rounded animate-pulse">
{/* Text placeholder */}
</div>
</div>
</div>
);
};
export default UserCardSkeleton;import UserCard from './components/UserCard'
import UserCardSkeleton from './skeletons/UserCardSkeleton'
function UserProfile({ userId, isLoading }) {
if (isLoading) {
return <UserCardSkeleton />
}
return <UserCard userId={userId} />
}Three plugins are available, each targeting a different styling approach. All three generate skeletons that respond to CSS variable-based theming at runtime.
skelton-auto ./my-app --style tailwind- Preserves layout classes (
flex,grid,gap-,p-,w-,h-, etc.) - Strips visual classes (
bg-,text-,font-, hover/focus variants) - Placeholder color:
bg-[var(--skeleton-bg,#e5e7eb)]— responds to CSS variables - Card/container surface:
bg-[var(--skeleton-surface,#ffffff)] - Animation:
animate-pulse - No companion files needed — pure Tailwind classes
skelton-auto ./my-app --style inline- For components that use React inline
style={{}}props - Extracts layout-relevant styles (
display,flexDirection,gap,padding,width,height, etc.) - Strips visual styles (
color,background,fontSize,fontWeight, etc.) - Placeholder:
backgroundColor: 'var(--skeleton-bg, #e5e7eb)' - Injects a
<style>tag with keyframe animation and CSS token definitions into the generated file header - No external CSS files required
skelton-auto ./my-app --style css-modules- For components using
.module.cssfiles - Parses the source
.module.cssand strips visual-only rules, keeping layout/sizing - Generates a companion
ComponentSkeleton.module.csswith cleaned rules - Generates a shared
skeleton-tokens.css(plain CSS, not a module) for:roottoken definitions - Placeholder classes:
skeleton-text,skeleton-image,skeleton-container,skeleton-animate - Container backgrounds replaced with
var(--skeleton-surface, #ffffff)automatically
All generated skeletons are driven by four CSS custom properties. Set them on any ancestor element to switch themes at runtime — no regeneration needed.
| Variable | Default (light) | Default (dark) | Purpose |
|---|---|---|---|
--skeleton-bg |
#e5e7eb |
#4b5563 |
Placeholder bar / block color |
--skeleton-surface |
#ffffff |
#1f2937 |
Card / container background |
--skeleton-shimmer-from |
1 |
1 |
Pulse animation start opacity |
--skeleton-shimmer-to |
0.4 |
0.3 |
Pulse animation end opacity |
Configure the theme in skelton.config.js:
module.exports = {
style: 'tailwind',
theme: {
mode: 'light', // 'light' | 'dark' | 'auto' | 'custom'
}
};| Mode | Behavior |
|---|---|
light |
Emits light token defaults in :root |
dark |
Emits dark token defaults in :root |
auto |
Emits light defaults + @media (prefers-color-scheme: dark) override |
custom |
Use your own token values for light and/or dark |
module.exports = {
style: 'css-modules',
theme: { mode: 'auto' }
};Generates:
:root {
--skeleton-bg: #e5e7eb;
--skeleton-surface: #ffffff;
--skeleton-shimmer-from: 1;
--skeleton-shimmer-to: 0.4;
}
@media (prefers-color-scheme: dark) {
:root {
--skeleton-bg: #4b5563;
--skeleton-surface: #1f2937;
--skeleton-shimmer-from: 1;
--skeleton-shimmer-to: 0.3;
}
}module.exports = {
style: 'tailwind',
theme: {
mode: 'custom',
tokens: {
light: {
bg: '#f0f0f0',
surface: '#ffffff',
shimmerFrom: 1,
shimmerTo: 0.5,
},
dark: {
bg: '#374151',
surface: '#1f2937',
shimmerFrom: 1,
shimmerTo: 0.2,
},
},
},
};You can switch themes at runtime by setting the CSS variables on a wrapper element — no page reload required:
const THEMES = {
light: {
'--skeleton-bg': '#e5e7eb',
'--skeleton-surface': '#ffffff',
'--skeleton-shimmer-from': '1',
'--skeleton-shimmer-to': '0.4',
},
dark: {
'--skeleton-bg': '#4b5563',
'--skeleton-surface': '#1f2937',
'--skeleton-shimmer-from': '1',
'--skeleton-shimmer-to': '0.3',
},
};
function App() {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
return (
<div style={THEMES[theme] as React.CSSProperties}>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
<UserCardSkeleton />
</div>
);
}The tool preserves these layout-related classes:
| Category | Prefixes |
|---|---|
| Flexbox | flex, items-, justify- |
| Grid | grid, col-, row- |
| Gap | gap-, space- |
| Padding | p-, px-, py-, pt-, pb-, pl-, pr- |
| Margin | m-, mx-, my-, mt-, mb-, ml-, mr- |
| Width/Height | w-, h-, min-, max- |
| Border | border, rounded |
| Shadow | shadow |
| Overflow | overflow- |
You can also use skelton-auto programmatically:
import {
loadConfig,
runPipeline,
analyzeFile,
generateSkeleton
} from 'skelton-auto/core';
import tailwindPlugin from 'skelton-auto/plugin-tailwind';
const config = await loadConfig('./skelton.config.js', {
projectPath: './my-project'
});
const result = await runPipeline(config, async (style) => {
return tailwindPlugin;
});
console.log(`Generated ${result.generated.length} skeletons`);skelton-auto/
├── packages/
│ ├── cli/ # CLI entry point
│ ├── core/ # Core functionality
│ │ ├── scanner.ts # File scanning
│ │ ├── parser.ts # AST parsing
│ │ ├── analyzer.ts # Component analysis
│ │ ├── generator.ts # Skeleton generation
│ │ ├── modifier.ts # Code injection
│ │ └── config.ts # Configuration
│ ├── plugin-tailwind/ # Tailwind CSS adapter
│ ├── plugin-css-modules/ # CSS Modules adapter
│ └── examples/ # Sample components
├── pnpm-workspace.yaml
├── turbo.json
└── README.md
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
# Clone the repository
git clone https://github.com/minaa66/skelton-auto.git
cd skelton-auto
# Install dependencies
npm install
# Build all packages (excludes example apps)
npm run build
# Link the CLI globally so you can run `skelton-auto` from anywhere
npm link
# Verify the link worked
skelton-auto --version
# Run against the example components (dry run)
npm run test:examples
# Run tests
npm testAfter npm link, you can run the CLI directly in any local project:
cd /path/to/your-project
skelton-auto . --style tailwind --dry-runTo unlink when you're done:
npm unlink -g skelton-autoIf you prefer not to link globally, run the built CLI directly with Node:
node packages/cli/dist/index.js ./packages/examples --dry-runMIT License - see LICENSE for details.
- Built with Babel for AST parsing
- Uses fast-glob for file scanning
- CLI powered by Commander.js
