Showing preview only (527K chars total). Download the full file or copy to clipboard to get everything.
Repository: birobirobiro/awesome-shadcn-ui
Branch: main
Commit: e0ca3df56210
Files: 128
Total size: 493.9 KB
Directory structure:
gitextract_2m4yzxwy/
├── .github/
│ ├── FUNDING.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── add-dates.yml
│ └── format-readme.yml
├── .gitignore
├── DEVELOPMENT.md
├── LICENSE
├── README.md
├── components.json
├── next.config.mjs
├── open-next.config.ts
├── package.json
├── postcss.config.mjs
├── scripts/
│ ├── add-dates.js
│ └── format-readme.js
├── src/
│ ├── app/
│ │ ├── [...not-found]/
│ │ │ └── page.tsx
│ │ ├── api/
│ │ │ ├── github/
│ │ │ │ ├── callback/
│ │ │ │ │ └── route.ts
│ │ │ │ └── device-flow/
│ │ │ │ └── route.ts
│ │ │ └── submit-resource/
│ │ │ └── route.ts
│ │ ├── bookmarks/
│ │ │ ├── error.tsx
│ │ │ └── page.tsx
│ │ ├── categories/
│ │ │ ├── [category]/
│ │ │ │ ├── [id]/
│ │ │ │ │ ├── error.tsx
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── error.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── error.tsx
│ │ │ └── page.tsx
│ │ ├── error.tsx
│ │ ├── global-error.tsx
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── not-found.tsx
│ │ └── page.tsx
│ ├── components/
│ │ ├── github-stars.tsx
│ │ ├── item-card.tsx
│ │ ├── item-grid.tsx
│ │ ├── kibo-ui/
│ │ │ └── theme-switcher/
│ │ │ └── index.tsx
│ │ ├── layout/
│ │ │ ├── footer.tsx
│ │ │ ├── header.tsx
│ │ │ └── page-header.tsx
│ │ ├── logo.tsx
│ │ ├── pagination-controls.tsx
│ │ ├── pr-submission-dialog.tsx
│ │ ├── search-filter-controls.tsx
│ │ ├── sections/
│ │ │ ├── cta-submit.tsx
│ │ │ ├── hero.tsx
│ │ │ ├── items-list.tsx
│ │ │ └── sponsorship.tsx
│ │ ├── sort.tsx
│ │ ├── sponsor-card.tsx
│ │ ├── sponsors/
│ │ │ ├── shadcn-blocks-logo.tsx
│ │ │ ├── shadcn-studio-logo.tsx
│ │ │ ├── shadcn-ui-kit-logo.tsx
│ │ │ └── sponsors.tsx
│ │ ├── theme-toggle.tsx
│ │ └── ui/
│ │ ├── accordion.tsx
│ │ ├── alert-dialog.tsx
│ │ ├── alert.tsx
│ │ ├── aspect-ratio.tsx
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── breadcrumb.tsx
│ │ ├── button-group.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── card.tsx
│ │ ├── carousel.tsx
│ │ ├── chart.tsx
│ │ ├── checkbox.tsx
│ │ ├── collapsible.tsx
│ │ ├── combobox.tsx
│ │ ├── command.tsx
│ │ ├── context-menu.tsx
│ │ ├── dialog.tsx
│ │ ├── direction.tsx
│ │ ├── drawer.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── empty.tsx
│ │ ├── field.tsx
│ │ ├── hover-card.tsx
│ │ ├── input-group.tsx
│ │ ├── input-otp.tsx
│ │ ├── input.tsx
│ │ ├── item.tsx
│ │ ├── kbd.tsx
│ │ ├── label.tsx
│ │ ├── marquee.tsx
│ │ ├── menubar.tsx
│ │ ├── multi-select.tsx
│ │ ├── native-select.tsx
│ │ ├── navigation-menu.tsx
│ │ ├── pagination.tsx
│ │ ├── popover.tsx
│ │ ├── progress.tsx
│ │ ├── radio-group.tsx
│ │ ├── resizable.tsx
│ │ ├── scroll-area.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── sidebar.tsx
│ │ ├── skeleton.tsx
│ │ ├── slider.tsx
│ │ ├── sonner.tsx
│ │ ├── spinner.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── textarea.tsx
│ │ ├── toggle-group.tsx
│ │ ├── toggle.tsx
│ │ └── tooltip.tsx
│ ├── hooks/
│ │ ├── use-bookmark.ts
│ │ ├── use-categories.ts
│ │ ├── use-debounce.ts
│ │ ├── use-github-auth.ts
│ │ ├── use-mobile.ts
│ │ ├── use-pr-submission.ts
│ │ ├── use-readme.ts
│ │ └── use-website-preview.ts
│ ├── lib/
│ │ ├── compose-refs.ts
│ │ ├── config.ts
│ │ ├── slugs.ts
│ │ └── utils.ts
│ └── providers/
│ ├── providers.tsx
│ └── theme-provider.tsx
├── tsconfig.json
└── wrangler.jsonc
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: ["https://buy.stripe.com/9B6cN6eZq6N136ygPm0Jq02"]
================================================
FILE: .github/pull_request_template.md
================================================
---
name: "feat: Add new awesome resource"
about: "Propose adding a new awesome resource related to shadcn/ui"
labels:
- feature
---
## Submission Options
You can submit resources in two ways:
1. **Via Website**: Use the [awesome-shadcn-ui.vercel.app](https://awesome-shadcn-ui.vercel.app/) submission form for a guided experience
2. **Via GitHub PR**: Use this template to create a pull request directly
## Describe the awesome resource you want to add
**What is it?**
> Briefly explain what this resource is and why it's awesome...
-
## **Which section does it belong to?**
- [ ] Libs and Components
- [ ] Plugins and Extensions
- [ ] Colors and Customizations
- [ ] Animations
- [ ] Tools
- [ ] Websites and Portfolios Inspirations
- [ ] Platforms
- [ ] Ports
- [ ] Design System
- [ ] Boilerplates / Templates
**Additional details (optional)**
> Include screenshots, demos, or any other useful context...
-
## **Checklist**
- [ ] I verified that the resource is listed in alphabetical order within its section.
- [ ] I checked that the resource is not already listed.
- [ ] I provided a clear and concise description of the resource.
- [ ] I included a valid and working link to the resource.
- [ ] I assigned the correct section to the resource.
**Important Notes:**
1. If you are introducing a new section, you must also add it to the `README.md` file and update the table of contents accordingly.
2. This repository focuses on open-source and freely accessible projects. Paid or fully commercial resources will not be accepted.
## Required Table Format
**Format your entry exactly like this:**
```markdown
| Name | Description | [Link](Your_Link_Here) |
```
**Table Structure:**
- **Name**: The resource name (keep it concise)
- **Description**: Brief description of what the resource does
- **Link**: Working link to the resource
- **Date**: Leave empty - our system adds this automatically when merged
**Formatting Rules:**
- Each entry must be on a single line
- Use proper markdown table syntax with `|` separators
- Don't add dates manually - the system handles this
- Ensure your link is working and accessible
Thank you for contributing to the awesome-shadcn/ui repository!
================================================
FILE: .github/workflows/add-dates.yml
================================================
name: Add Dates to New Resources
on:
push:
branches:
- main
paths:
- 'README.md'
jobs:
add-dates:
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Add dates to new resources
run: node scripts/add-dates.js
- name: Commit changes (if any)
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add README.md
git diff --quiet && git diff --staged --quiet || git commit -m "Add dates to new resources"
git push
================================================
FILE: .github/workflows/format-readme.yml
================================================
name: Format README
on:
pull_request:
paths:
- "README.md"
workflow_dispatch:
jobs:
format-readme:
runs-on: ubuntu-latest
steps:
- name: Checkout PR
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Format README
run: node scripts/format-readme.js
- name: Commit changes (if any)
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add README.md
git diff --quiet && git diff --staged --quiet || git commit -m "Format README.md"
# Empurra para a branch do PR
git push origin HEAD:${{ github.head_ref }}
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.open-next/
.agents
.claude
.mcp.json
================================================
FILE: DEVELOPMENT.md
================================================
# Development Guide
This guide covers the development setup, architecture, and configuration of the awesome-shadcn-ui website.
## Quick Start
```bash
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
```
## Project Structure
```
src/
├── app/ # Next.js App Router
│ ├── api/ # API routes
│ │ └── github/ # GitHub OAuth & PR submission
│ ├── globals.css # Global styles
│ └── layout.tsx # Root layout
├── components/ # React components
│ ├── ui/ # shadcn/ui components
│ ├── item-card.tsx # Resource card component
│ └── pr-submission-dialog.tsx
├── hooks/ # Custom React hooks
│ ├── use-bookmark.ts # Bookmark management
│ ├── use-debounce.ts # Search debouncing
│ ├── use-github-auth.ts # GitHub OAuth flow
│ ├── use-pr-submission.ts # PR creation logic
│ └── use-readme.ts # README parsing
├── lib/ # Utilities & configuration
│ ├── config.ts # Centralized config
│ └── utils.ts # Helper functions
└── providers/ # React context providers
├── theme-provider.tsx
└── providers.tsx
```
## Configuration
### Centralized Config (`src/lib/config.ts`)
All configuration is centralized in one file:
```typescript
export const GITHUB_CONFIG = {
CLIENT_ID: "Ov23lizgfZ4yKq0NxcTm", // GitHub OAuth App
REPO_OWNER: "birobirobiro", // Repository owner
REPO_NAME: "awesome-shadcn-ui", // Repository name
DEVICE_FLOW_URL: "https://github.com/login/device/code",
ACCESS_TOKEN_URL: "https://github.com/login/oauth/access_token",
SCOPES: ["repo"], // Required permissions
FORK_CREATION_DELAY: 5000, // Delay for fork creation
};
export const PR_TEMPLATE = {
HEADER: { /* PR template structure */ },
CATEGORIES: [ /* Available categories */ ],
CHECKLIST_ITEMS: { /* Automated checklist */ }
};
export const ERROR_MESSAGES = { /* Error messages */ };
export const STATUS_MESSAGES = { /* Status messages */ };
```
## Key Features
### Resource Display
- **Source**: Fetches from GitHub README.md
- **Parsing**: `use-readme.ts` hook parses markdown tables
- **Caching**: 30-minute cache to reduce API calls
- **Categories**: Automatically groups by README sections
### Search & Filtering
- **Real-time search** with debouncing (300ms)
- **Category filtering** with URL state management
- **Layout switching** (compact, grid, row)
- **Bookmark system** with localStorage persistence
### PR Submission System
- **GitHub OAuth**: Device flow for secure authentication
- **One-time access**: No credential storage
- **Automated workflow**:
1. Check/create user fork
2. Create feature branch
3. Update README with new resource
4. Create pull request with template
- **Duplicate prevention**: Checks existing resources
- **Alphabetical sorting**: Maintains README organization
### GitHub Integration
#### OAuth Flow (`use-github-auth.ts`)
```typescript
// 1. Start device flow
const { userCode, verificationUri } = await startDeviceFlow();
// 2. User authorizes on GitHub
// 3. Poll for access token
// 4. Get user info and create authenticated Octokit
```
#### PR Creation (`use-pr-submission.ts`)
```typescript
// 1. Check/create fork
// 2. Create branch from latest commit
// 3. Fetch latest README from upstream
// 4. Insert new resource alphabetically
// 5. Commit changes
// 6. Create PR with template
```
## UI Components
### shadcn/ui Integration
- **Components**: Button, Dialog, Input, Select, Badge, etc.
- **Theming**: Dark/light mode with next-themes
- **Styling**: Tailwind CSS with custom design system
- **Accessibility**: Built-in ARIA support
### Custom Components
- **ItemCard**: Displays resource information
- **PRSubmissionDialog**: Handles GitHub authentication and form
- **LayoutSwitcher**: Toggle between view modes
- **SearchBar**: Real-time search with debouncing
## Data Flow
```
GitHub README.md → use-readme.ts → Resource[] → UI Components
↓
User Submission → PR Dialog → GitHub OAuth → PR Creation
```
## Development Workflow
### Adding New Features
1. **Hooks**: Add custom logic in `src/hooks/`
2. **Components**: Create reusable components in `src/components/`
3. **API**: Add endpoints in `src/app/api/`
4. **Config**: Update centralized config in `src/lib/config.ts`
### Environment Variables
```bash
# No environment variables required
# All config is in src/lib/config.ts
```
### Testing
```bash
# Run type checking
npm run type-check
# Run linting
npm run lint
# Build check
npm run build
```
## Dependencies
### Core
- **Next.js 15.2.4**: React framework with App Router
- **React 19**: UI library
- **TypeScript 5.8.3**: Type safety
### UI & Styling
- **shadcn/ui**: Component library (Radix UI primitives)
- **Tailwind CSS 4.1.11**: Utility-first CSS
- **next-themes 0.4.6**: Theme management
- **Lucide React 0.509.0**: Icons
- **Framer Motion 11.0.0**: Animations
### GitHub Integration
- **@octokit/rest 22.0.0**: GitHub API client
- **Device Flow OAuth**: Secure authentication
### Utilities
- **clsx 2.1.1**: Conditional classes
- **tailwind-merge 2.6.0**: Tailwind class merging
- **sonner 1.7.4**: Toast notifications
- **class-variance-authority 0.7.1**: Component variants
- **cmdk 1.0.0**: Command palette
## Contributing
1. **Fork the repository**
2. **Create feature branch**: `git checkout -b feature/amazing-feature`
3. **Make changes** following the existing patterns
4. **Test thoroughly** with different scenarios
5. **Submit PR** with clear description
## Architecture Decisions
### Why Device Flow OAuth?
- **Security**: No client secrets in frontend
- **User-friendly**: No redirects, works everywhere
- **Temporary**: One-time access, no permanent storage
### Why Centralized Config?
- **Maintainability**: Single source of truth
- **Type Safety**: TypeScript constants
- **Consistency**: Same values across all files
### Why README as Data Source?
- **User Readme View**: Easily User Viewable
- **Simplicity**: No database required
- **Version Control**: Changes tracked in Git
- **Transparency**: Public data source
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 João Inácio Neto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center">
<a href="https://awesome-shadcn-ui.vercel.app" target="_blank">
<img width="400" src="public/logo.svg" alt="awesome-shadcn/ui logo">
</a>
</p>
<h1 align="center">awesome-shadcn/ui</h1>
<p align="center">
A curated list of awesome things related to <a href="https://ui.shadcn.com/" target="_blank">shadcn/ui</a>
</p>
<p align="center">
<i>Created by <a href="https://birobirobiro.dev/" target="_blank">birobirobiro.dev</a></i><br>
<i>Site by <a href="https://bankkroll.xyz/" target="_blank">bankkroll.xyz</a></i><br>
<i>Sponsored by</i><br>
<a href="https://shadcnstudio.com/?utm_source=awesome-shadcn-ui&utm_medium=banner&utm_campaign=github" target="_blank">
<img src="public/sponsors/shadcnstudio.svg" alt="shadcnstudio.com" width="32">
</a>
<a href="https://www.shadcnblocks.com" target="_blank">
<img src="public/sponsors/shadcnblocks.svg" alt="shadcnblocks.com" width="32">
</a>
<a href="https://shadcnuikit.com/?utm_source=awesome-shadcn-ui&utm_medium=banner&utm_campaign=github" target="_blank">
<img src="public/sponsors/shadcnuikit.svg" alt="shadcnuikit.com" width="32">
</a>
</p>
<p align="center">
<a href="https://awesome-shadcn-ui.vercel.app/" target="_blank">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome">
</a>
</p>
## Libs and Components
| Name | Description | Link | Date |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------- |
| 21st.dev | Open source npm for shadcn/ui components. Also: Dribble for design engineers. Install UI components via shadcn CLI, or publish your own. | [Link](https://21st.dev/) | 2024-12-06 |
| 8bitcn.com | A set of retro-designed, accessible components and a code distribution platform. Open Source. Open Code. | [Link](https://www.8bitcn.com/) | 2025-04-12 |
| aceternity-ui | Copy paste the most trending react components without having to worry about styling and animations. | [Link](https://ui.aceternity.com/) | 2024-12-06 |
| agents-ui | Agents UI is LiveKit’s open source component library built with React and shadcn for designing voice agent interfaces. Start with production-ready defaults, then customize every detail to match your brand. | [Link](https://livekit.io/ui) | 2026-03-04 |
| animated-header | Vercel-like animated header. | [Link](https://github.com/mehrdadrafiee/animated-header) | 2025-10-20 |
| animated-tabs | Vercel-like animated tabs. | [Link](https://github.com/mehrdadrafiee/animated-tabs) | 2025-04-06 |
| assistant-ui | React Components for AI Chat. | [Link](https://github.com/Yonom/assistant-ui) | 2024-09-23 |
| audio/ui | A set of accessible and composable Audio UI components. Built on top of shadcn/ui, it's designed for you to copy, paste, and own. | [Link](https://github.com/ouestlabs/audio-ui) | 2025-11-20 |
| autocomplete-select-shadcn-ui | Autocomplete component built with shadcn/ui and Fancy Multi Select by Maximilian Kaske. | [Link](https://www.armand-salle.fr/post/autocomplete-select-shadcn-ui) | 2024-04-07 |
| auto-form | A React component that automatically creates a shadcn/ui form based on a zod schema. | [Link](https://github.com/vantezzen/auto-form) | 2024-04-29 |
| async-select | Async Select component built with shadcn/ui with debounce search. | [Link](https://async.rdsx.dev) | 2024-07-22 |
| berlix | Animated components library built using Tailwind CSS and Motion | [Link](https://berlix.vercel.app) | 2025-06-10 |
| big-calendar | A modern, feature-rich calendar application with multiple viewing options built using Next.js, TypeScript, and Tailwind CSS. | [Link](https://github.com/lramos33/big-calendar) | 2025-03-08 |
| billingsdk | Modern, type-safe billing and subscription management components for React, built with TypeScript and Tailwind CSS. Designed to work seamlessly alongside shadcn/ui by [Dodo Payments](https://dodopayments.com/). [GitHub](https://github.com/dodopayments/billingsdk) | [Link](https://billingsdk.com/) | 2025-09-06 |
| billui | Open source billing components for React built with Shadcn. Pricing cards, payment methods, invoice history and more. | [Link](https://github.com/commet-labs/billui) | 2025-12-25 |
| blocks.so | A set of clean, modern building blocks to copy and paste into your apps. Works with all React frameworks. | [Link](https://github.com/ephraimduncan/blocks) | 2025-09-06 |
| buouui | A UI component library and template suite based on shadcn/ui with stunning landing pages, templates, and rich animations. | [Link](https://buouui.com) | 2025-04-06 |
| bundui | A collection of reusable animated components built with Tailwind CSS and Framer Motion. | [Link](https://bundui.io) | 2024-09-23 |
| calendar | React/shadcn full calendar like Google Calendar | [Link](https://github.com/charlietlamb/calendar) | 2024-05-03 |
| calendar-cn | A beautifully crafted calendar component built with shadcn/ui and Tailwind CSS, inspired by Notion Calendar. Week view, dark mode, and more. | [Link](https://github.com/vmnog/calendarcn) | 2026-03-05 |
| capture-photo | Browser-based React component for camera functionalities in web applications. | [Link](https://github.com/UretzkyZvi/capture-photo) | 2024-05-06 |
| carouselcn | Copy-paste carousel components and examples for your react apps | [Link](https://github.com/mnove/carouselcn) | 2026-01-27 |
| cascader-shadcn | A cascading dropdown menu component for selecting hierarchical data like locations, categories, or organizational structures. | [Link](https://github.com/Ademking/cascader-shadcn) | 2025-12-04 |
| chanhdai-components | A collection of reusable components. Trusted registry for shadcn/ui. | [Link](https://chanhdai.com/components) | 2026-03-01 |
| clerk-elements | Composable components for building custom UIs on top of Clerk's APIs. | [Link](https://clerk.com/docs/elements/examples/shadcn-ui) | 2024-06-07 |
| clerk-shadcn-theme | Synchronize Clerk SignIn/SignUp components with shadcn/ui styles. | [Link](https://github.com/stormynight9/clerk-shadcn-theme) | 2024-06-07 |
| commerce-ui | Components, blocks and examples to build e-commerce storefronts and apps. | [Link](https://github.com/stackzero-labs/ui) | 2025-02-20 |
| confirm-dialog | A confirm dialog component built with shadcn/ui. | [Link](https://github.com/Aslam97/react-confirm-dialog) | 2024-07-02 |
| country-state-dropdown | Component built with Nextjs, Tailwindcss, shadcn/ui & Zustand. | [Link](https://github.com/Jayprecode/country-state-dropdown) | 2024-02-22 |
| creatorem/ui | Missing awesome shadcn components (like Tour, Stepper, QRCode Motion Dialog -> to imitate native behaviour ...) built on top of radix primitives and motion. | [Link](https://github.com/creatorem/ui) | 2025-12-19 |
| credenza | Ready-made responsive modal component for shadcn/ui. | [Link](https://github.com/redpangilinan/credenza) | 2024-06-07 |
| crypto-charts | Crypto charts made for shadcn/ui using PythNetwork. | [Link](https://github.com/jstnw10/crypto-charts) | 2024-07-16 |
| cult-ui | Curated set of animated shadcn-style React components. | [Link](https://www.cult-ui.com/) | 2024-05-29 |
| custom-admin-dashboard | A minimal, open-source ecommerce admin dashboard template built with shadcn/ui and Next.js. Includes products, products creation and details page, order, customer, and settings pages. | [Link](https://github.com/S5SAJID/custom-ecom) | 2025-09-06 |
| data-command | Component for building dynamic command palettes with API-powered data. | [Link](https://shadcn.davidsling.in/components/data-command) | 2025-09-06 |
| date-range-picker-for-shadcn | Multi-month views, text entry, preset ranges, responsive design, and date range comparisons. | [Link](https://github.com/johnpolacek/date-range-picker-for-shadcn) | 2024-04-29 |
| date-time-picker-shadcn | Datetime Picker for shadNext Project. | [Link](https://shadcn-datetime-picker.vercel.app) | 2024-07-16 |
| date-time-range-picker-shadcn | Fully featured date-time range picker with multi-month views, timezone support, preset ranges, and modular components for date and time selection. | [Link](https://date-time-range-picker.vercel.app/) | 2025-03-08 |
| datetime-picker | Datetime picker with timezone support, min/max dates, and month/year selection. | [Link](https://shadcn-datetime-picker-xi.vercel.app) | 2024-07-16 |
| dnd-dashboard | Dashboard with drop-to-swap layouts using Next.js, shadcn/ui, and swapy. | [Link](https://github.com/olliethedev/dnd-dashboard) | 2024-10-17 |
| downshift-shadcn-combobox | Combobox/autocomplete component built with shadcn/ui and Downshift. | [Link](https://github.com/TheOmer77/downshift-shadcn-combobox) | 2024-06-27 |
| drag-to-resize-sidebar | Extended shadcn/ui sidebar component with persisted state drag-to-resize functionality. | [Link](https://github.com/lumpinif/drag-to-resize-sidebar) | 2024-11-21 |
| druid/ui | Intercom inspired AI chatbot and UI components built on shadcn/ui. | [Link](https://druidui.com/) | 2024-11-21 |
| drop-drawer | A dropdown menu on desktop and a drawer on mobile devices. | [Link](https://github.com/jiaweing/DropDrawer) | 2025-05-13 |
| dy-comps | shacn/ui & Framer Motion React components — flexible, responsive & easy to drop into any project. | [Link](https://dycomps.oimmi.com/) | 2025-03-08 |
| dynamic-svg-arrow | Connect any two elements with beautiful, animated paths. Control curve shape, routing, layering, arrowheads, gradients, and responsive behavior in real-time. | [Link](https://dsa.hncore.website/) | 2025-10-09 |
| echo-editor | Modern WYSIWYG rich-text editor based on tiptap and shadcn/ui. | [Link](https://github.com/Seedsa/echo-editor) | 2024-06-07 |
| edil-ozi | React components with Gsap, framer motion, and tailwind. | [Link](https://edilozi.pro/) | 2024-06-27 |
| eldora-ui | free and open-source animated components built with React, Typescript, Tailwind CSS, and Framer Motion. | [Link](https://eldoraui.site/) | 2024-12-06 |
| emblor | Customizable, accessible tag input component with shadcn/ui. | [Link](https://github.com/JaleelB/emblor) | 2024-04-29 |
| enhanced-button | Enhanced version of the default shadcn-button component. | [Link](https://github.com/jakobhoeg/enhanced-button) | 2024-04-29 |
| envin | Framework-agnostic, type-safe tool to validate and preview your environment variables - powered by your favorite schema validator. | [Link](https://envin.turbostarter.dev) | 2025-07-07 |
| eo-n/ui | Enhanced UI components built on shadcn’s robust foundation, integrated with Base UI and Tailwind CSS for a modern and customizable design system. | [Link](https://github.com/aeonzz/eo-n) | 2025-04-04 |
| event-timeline-roadmap | A pair of customizable, animated event timeline and roadmap components | [Link](https://roadmap.hncore.website/) | 2025-04-01 |
| extend-ui | Reusable components built on shadcn/ui for web applications. | [Link](https://www.extend-ui.com/) | 2024-11-28 |
| fancy-area | Textarea with @mention support inspired by GitHub's PR comment section. | [Link](https://craft.mxkaske.dev/post/fancy-area) | 2024-06-27 |
| fancy-box | GitHub PR label selector-inspired Combobox with radix-ui components. | [Link](https://craft.mxkaske.dev/post/fancy-box) | 2024-07-11 |
| fancy-multi-select | Multi Select Component inspired by campsite.design and cal.com. | [Link](https://craft.mxkaske.dev/post/fancy-multi-select) | 2024-04-29 |
| fancy-switch | Fancy switch component built with shadcn/ui. | [Link](https://github.com/Aslam97/react-fancy-switch) | 2024-07-11 |
| farmui | Styled and animated component library with npm package support. | [Link](https://farmui.com) | 2024-06-08 |
| file-uploader | File uploader with shadcn/ui and react-dropzone. | [Link](https://github.com/sadmann7/file-uploader) | 2024-06-07 |
| file-vault | File upload component for React. | [Link](https://github.com/ManishBisht777/file-vault) | 2024-06-07 |
| floating-dragable-card | Dragable and resizable card using shadcn/ui elements. | [Link](https://github.com/nishansanjuka/react-drag-card) | 2024-11-13 |
| fusion-ui | Library combining shadcn/ui and MagicUI. | [Link](https://github.com/nyxb-ui/ui) | 2024-07-02 |
| glasscn-ui | shadcn/ui component library with glassmorphism variants, and many additional components. | [Link](https://github.com/itsjavi/glasscn-ui) | 2025-06-10 |
| glitchcn-ui | A terminal-styled cyberpunk component library for Next.js with scanline effects, glowing borders, and monospace typography. | [Link](https://glitchcn-ui.vercel.app/) | 2026-02-06 |
| gluestack-ui | React & React Native Components with Tailwind CSS. | [Link](https://gluestack.io) | 2024-11-08 |
| goey-toast | Morphing toast notifications for React. Organic blob animations, promise tracking, and full customization out of the box | [Link](https://goey-toast.vercel.app/) | 2026-02-14 |
| heroicons-animated | An open-source collection of 316 beautifully animated heroicons for your projects. | [Link](https://heroicons-animated.vercel.app/) | 2026-01-23 |
| hexta-ui | Build stunning websites effortlessly. Modern, responsive, and customizable UI components for Next.js. Copy, adapt, and personalize them. | [Link](https://hextaui.com) | 2025-05-14 |
| ibelick/background-snippet | Ready to use collection of modern background snippets. | [Link](https://bg.ibelick.com/) | 2024-06-09 |
| image-crop-field | Image crop field with shadcn/ui. This component is a wrapper around the react-easy-crop component. | [Link](https://github.com/JsCodeDevlopment/upload-crop-image) | 2025-10-01 |
| image-upload-shadcn | Image upload component. | [Link](https://github.com/kushagrasarathe/image-upload-shadcn) | 2024-10-22 |
| indie-ui | UI components with variants. | [Link](https://github.com/Ali-Hussein-dev/indie-ui) | 2024-06-07 |
| inspira-ui | UI components for animated interfaces in Vue/NuxtJS. | [Link](https://inspira-ui.com/) | 2024-10-22 |
| jolyui | JolyUI is a modern React component library built with TypeScript, Tailwind CSS and Motion. It offers a wide range of customizable and accessible UI components to help you build stunning web applications quickly and efficiently. | [Link](https://github.com/Johuniq/jolyui) | 2026-01-23 |
| junwen-k/ui-x | Additional beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source. | [Link](https://ui-x.junwen-k.dev/) | 2025-02-03 |
| kanban-board | A production‑ready Kanban board built on shadcn/ui with React & Tailwind CSS: zero dependencies, drag‑and‑drop, keyboard accessibility and seamless theming. | [Link](https://shadcn-kanban-board.com/) | 2025-07-15 |
| kibo-ui | Kibo UI is designed to be a more comprehensive library of components that can be used to build more complex applications. | [Link](https://www.kibo-ui.com/overview) | 2025-04-04 |
| kokonut-ui | Free Modern and Customizable components for Next.js. | [Link](https://kokonutui.com/) | 2024-11-08 |
| ktui | Open-source collection of customizable UI components for Tailwind CSS and vanilla JavaScript | [Link](https://github.com/keenthemes/ktui) | 2025-06-10 |
| launch-ui | Landing page components with React, Shadcn/ui and Tailwind. | [Link](https://www.launchuicomponents.com/) | 2024-11-12 |
| lingua-time | Smart datetime picker with natural language input. | [Link](https://github.com/nainglinnkhant/lingua-time) | 2024-10-22 |
| linked-chart | Chart component linked with data-table. | [Link](https://github.com/ardasisbot/linked-chart) | 2025-02-03 |
| lukacho-ui | Next Generation UI Components. | [Link](https://ui.lukacho.com/components) | 2024-07-02 |
| magicui | React components for landing pages with tailwindcss + framer motion. | [Link](https://magicui.design) | 2024-04-25 |
| maily.to | Notion-like powerful email editor. | [Link](https://github.com/arikchakma/maily.to) | 2024-06-15 |
| manfromexistence-ui | Components to build beautiful designs. | [Link](https://github.com/manfromexistence/ui) | 2024-12-26 |
| manifest-ui | Components for ChatGPT Apps and MCP Apps. | [Link](https://ui.manifest.build) | 2025-12-23 |
| matsu-theme | Ghibli Studio inspired theme for shadcn/ui made by Matt Wierzbicki | [Link](https://matsu-theme.vercel.app/) | 2025-04-23 |
| mindmapcn | Beautiful mind maps, works seamlessly with shadcn/ui. | [Link](https://github.com/SSShooter/mindmapcn) | 2026-01-27 |
| minimal-tiptap | Minimal WYSIWYG editor with shadcn/ui and tiptap. | [Link](https://github.com/Aslam97/shadcn-minimal-tiptap) | 2024-06-22 |
| mixcnui | Collection of animated components for Nextjs. | [Link](https://github.com/taqui-786/mixcnui) | 2024-07-07 |
| modal-control-query | A hook to control shadcn modal components using query params | [Link](https://shadcn.davidsling.in/hooks/use-modal-control-query) | 2025-11-02 |
| moleculeui | A modern React component library focused on intuitive interactions and seamless user experiences. | [Link](https://www.moleculeui.design/) | 2025-09-06 |
| multi-selection | Managing multi-selection functionality with highlighter. | [Link](https://github.com/sherifawad/multi-selection-with-add-remove) | 2025-01-21 |
| mvpblocks | Copy-paste beautiful, responsive components without worrying about styling or animations. Build faster, launch sooner. | [Link](https://blocks.mvp-subha.me) | 2025-05-14 |
| mynaui | TailwindCSS and shadcn/ui UI Kit for Figma and React. | [Link](https://mynaui.com/) | 2024-06-22 |
| neobrutalism-components | Neobrutalism-styled Tailwind React and shadcn/ui components. | [Link](https://github.com/ekmas/neobrutalism-components) | 2024-04-07 |
| nextjs-components | Next.js components with TypeScript and shadcn/ui. | [Link](https://components.bridger.to/) | 2024-06-07 |
| nextjs-dnd | Sortable Drag and Drop with Next.js and dnd-kit. | [Link](https://github.com/sujjeee/nextjs-dnd) | 2024-04-09 |
| nextjs-link-pagination | Pagination using Nextjs Links and search params. | [Link](https://shadcn-next-link-pagination.vercel.app) | 2024-07-30 |
| nextjs-multi-image-upload | Compact, responsive file uploader with shadcn/ui, React Hook Form, and cloud support (S3/R2). | [Link](https://github.com/jacksonkasi0/nextjs-multi-image-upload) | 2025-02-23 |
| next-shadcn-dashboard-starter | Admin Dashboard Starter with Nextjs 14 and Shadcn UI. | [Link](https://github.com/Kiranism/next-shadcn-dashboard-starter) | 2024-06-06 |
| next-stepper | Dynamic multi-step form with Next.js and zustand. | [Link](https://github.com/ebulku/next-stepper) | 2024-11-25 |
| novel | Notion-style WYSIWYG editor with AI-powered autocompletion. | [Link](https://github.com/steven-tey/novel) | 2024-06-11 |
| number-flow | React component for number transitions and formatting. | [Link](https://number-flow.barvian.me/) | 2024-10-17 |
| origin-ui | Beautiful UI components with Tailwind CSS and Next.js. | [Link](https://originui.com/) | 2024-10-28 |
| password-input | shadcn/ui custom password input. | [Link](https://gist.github.com/mjbalcueva/b21f39a8787e558d4c536bf68e267398) | 2024-03-28 |
| payment-gateways | Integration of payment gateways with Next.js 14. | [Link](https://github.com/PremPrakashCodes/payment-gateways) | 2024-08-05 |
| pdfx | shadcn/ui-style PDF component library for React. Copy-paste components built on @react-pdf/renderer run pdfx add invoice and own the code. | [Link](https://github.com/akii09/pdfx) | 2026-02-28 |
| phone-input-shadcn-ui | Custom phone number component with shadcn/ui. | [Link](https://www.armand-salle.fr/post/phone-input-shadcn-ui) | 2024-06-07 |
| pittaya-ui | A fully open-source UI library for React, powered by TypeScript and Tailwind CSS. Fast, composable, and ready for production. | [Link](https://github.com/pittaya-ui/ui-kit) | 2026-01-27 |
| planner | Adaptable scheduling component for React. | [Link](https://github.com/UretzkyZvi/planner) | 2024-04-29 |
| plate | AI-powered rich-text editor. | [Link](https://github.com/udecode/plate) | 2024-03-21 |
| plate-select-editor | Rich multi-select editor. | [Link](https://platejs.org/docs/multi-select) | 2024-11-28 |
| pqoqubbw | Open-source animated icons collection. | [Link](https://icons.pqoqubbw.dev/) | 2024-11-21 |
| pricing-page-shadcn | Customizable pricing page with Next.js 14. | [Link](https://github.com/m4nute/pricing-page-shadcn) | 2024-04-07 |
| progress-button | Extended button component with progress UX. | [Link](https://github.com/tomredman/ProgressButton) | 2024-07-10 |
| re-ui | Open-source collection of UI components and animated effects built with React, Typescript, Tailwind CSS, and Motion. Pairs beautifully with shadcn/ui | [Link](https://reui.io/) | 2025-09-06 |
| react-dnd-kit-tailwind-shadcn-ui | Accessible kanban board with dnd-kit. | [Link](https://github.com/Georgegriff/react-dnd-kit-tailwind-shadcn-ui) | 2024-03-27 |
| react-highlight-popover | Headless component for text selection popovers. | [Link](https://react-highlight-popover.omsimos.com) | 2024-10-03 |
| react-pdf-flipbook-viewer | PDF flipbook viewer with zoom and fullscreen. | [Link](https://github.com/mohitkumawat310/react-pdf-flipbook-viewer) | 2024-12-26 |
| react-select | React-select library with shadcn styling. | [Link](https://gist.github.com/ilkou/7bf2dbd42a7faf70053b43034fc4b5a4) | 2024-07-22 |
| react-wheel-picker | iOS-like wheel picker for React with smooth inertia scrolling and infinite loop support. | [Link](https://react-wheel-picker.chanhdai.com) | 2025-09-11 |
| recursive-dnd-kanban-board | Recursive drag and drop kanban board. | [Link](https://github.com/mehrdadrafiee/recursive-dnd-kanban-board) | 2024-10-03 |
| retro-ui | An open source component library, inspred by neo brutalism design system | [Link](https://retroui.dev) | 2025-05-13 |
| roadmap-ui | Components for interactive roadmaps. | [Link](https://github.com/haydenbleasel/roadmap-ui) | 2024-12-26 |
| ruixen-ui | Ruixen UI: Lightweight & Customizable React Library | [Link](https://github.com/ruixenui/ruixen.com) | 2025-12-04 |
| search-address | Interactive address search using OpenStreetMap. | [Link](https://github.com/UretzkyZvi/search-address) | 2024-05-07 |
| shadboard | An admin dashboard template built with Next.js 15, React 19, Tailwind CSS v4, and Shadcn/UI components, featuring starter and full kits for scalable, user-friendly web apps. | [Link](https://github.com/Qualiora/shadboard) | 2025-04-22 |
| shadcn-address-autocomplete | Address autocomplete with Google Places API. | [Link](https://github.com/NiazMorshed2007/shadcn-address-autocomplete) | 2024-07-10 |
| shadcn-admin | Admin Dashboard UI with shadcn/ui and Vite. | [Link](https://github.com/satnaing/shadcn-admin) | 2024-12-27 |
| shadcn-admin-kit | Powerful open-source shadcn components to build beautiful internal tools, admin panels, and dashboards with React | [Link](https://github.com/marmelab/shadcn-admin-kit) | 2025-07-07 |
| shadcn-blocks | Official pre-made customizable components. | [Link](https://ui.shadcn.com/blocks) | 2024-03-27 |
| shadcn-blocks-com | Hundreds of extra blocks built with shadcn/ui. | [Link](https://www.shadcnblocks.com) | 2025-02-23 |
| shadcn-builder | Create beautiful, responsive forms with the easy-to-use form builder and generate React code using shadcn/ui components. | [Link](https://www.shadcn-builder.com/?utm_source=github&utm_content=awesome-shadcn-ui) | 2025-03-31 |
| shadcn-cal | Cal.com monthly calendar replica with shadcn/ui. | [Link](https://shadcn-cal-com.vercel.app/?date=2024-04-29) | 2024-05-03 |
| shadcn-calendar-heatmap | Modern calendar heatmap alternative. | [Link](https://shadcn-calendar-heatmap.vercel.app/) | 2024-07-02 |
| shadcn-calendar-component | Calendar date picker component. | [Link](https://github.com/sersavan/shadcn-calendar-component) | 2024-07-02 |
| shadcn-chat | Customizable chat component. | [Link](https://github.com/jakobhoeg/shadcn-chat) | 2024-04-29 |
| shadcn-carousel-testimonials | Carousel Testimonials component. | [Link](https://github.com/johanguse/shadcn-carousel-testimonials) | 2024-07-16 |
| shadcn-chatbot-kit | Customizable chatbot components. | [Link](https://shadcn-chatbot-kit.vercel.app/) | 2024-12-27 |
| shadcn-color-picker | Color picker with react-color. | [Link](https://shadcn-color-picker.vercel.app/) | 2024-09-23 |
| shadcn-cookies | Sleek and flexible cookie consent component, designed with shadcn/ui | [Link](https://shadcn-cookies.vercel.app/) | 2025-02-06 |
| shadcn-cookie-consent | Customizable cookie consent component. | [Link](https://github.com/r2hu1/shadcn-cookie-consent) | 2024-10-17 |
| shadcn-components-blocks | The ultimate blocks and components for Shadcn UI & Tailwind CSS. | [Link](https://shadcncomponents.dev/) | 2025-09-06 |
| shadcn-country-dropdown | ISO 3166 country selector dropdown. | [Link](https://shadcn-country-dropdown.vercel.app/) | 2024-12-27 |
| shadcn-data-table-advanced-col-opions | DataTable with column resizing. | [Link](https://github.com/danielagg/shadcn-data-table-advanced-col-opions) | 2024-04-07 |
| shadcn-date-picker | Advanced date picker with various features. | [Link](https://date-picker.luca-felix.com) | 2024-07-25 |
| shadcn-drag-and-drop-sort | Drag-and-drop sortable list of pills of different widths using dnd-kit. | [Link](https://github.com/crystaltai/shadcn-drag-and-drop) | 2025-02-03 |
| shadcn-drag-table | Drag-and-drop table component. | [Link](https://github.com/zenoncao/shadcn-drag-table) | 2024-02-22 |
| shadcn-dropzone | File upload component using React-Dropzone, built with accessibility in mind. | [Link](https://github.com/janglad/shadcn-dropzone) | 2025-01-13 |
| shadcn-editor | Lexical editor with shadcn theme. | [Link](https://github.com/htmujahid/shadcn-editor) | 2024-11-08 |
| shadcn-examples | Dozens of advanced shadcn/ui examples. Easily integrate sample applications and components into your project. | [Link](https://shadcnexamples.com) | 2025-06-04 |
| shadcn-extends | Collection of shadcn/ui components. | [Link](https://github.com/lucioew28/extends) | 2024-06-07 |
| shadcn-extension | Open-source component collection. | [Link](https://github.com/BelkacemYerfa/shadcn-extension) | 2024-06-07 |
| shadcn-font-picker | Font picker using shadcn/ui components and google font API. | [Link](https://shadcn-font-picker.vercel.app) | 2025-04-22 |
| shadcn-full-calendar | A feature-rich calendar application built with React, TypeScript, and ShadCN UI components. This project provides a customizable and interactive calendar experience with multiple views, event management, and a modern UI. | [Link](https://github.com/yassir-jeraidi/full-calendar) | 2025-07-07 |
| shadcn-iconpicker | React/shadcn simple icon picker using lucide icons. | [Link](https://icon-picker.alan-courtois.fr/) | 2025-02-20 |
| shadcn-image-cropper | Image cropper with react-image-crop. | [Link](https://github.com/sujjeee/shadcn-image-cropper) | 2024-11-08 |
| shadcn-io | Advanced Shadcn/UI components. | [Link](https://shadcn.io) | 2025-09-06 |
| shadcn-linear-combobox | Linear-style task priority combobox. | [Link](https://github.com/damianricobelli/shadcn-linear-combobox) | 2024-04-25 |
| shadcn-location-picker | Simple google maps location picker. | [Link](https://github.com/brielov/shadcn-location-picker) | 2025-03-04 |
| shadcn-map | A map component built for shadcn/ui using Leaflet and React Leaflet. | [Link](https://shadcn-map.vercel.app/) | 2025-09-30 |
| shadcn-multi-select-component | Multi-select component. | [Link](https://github.com/sersavan/shadcn-multi-select-component) | 2024-06-07 |
| shadcn-number-scrubber | Draggable numeric input component. | [Link](https://github.com/camwebby/shadcn-react-number-scrubber) | 2025-01-07 |
| shadcn-packaged | This is an npm package that exports all shadcn/ui components without the need for a CLI, designed for ease of use. | [Link](https://github.com/anuoua/shadcn-packaged) | 2025-02-27 |
| shadcn-phone-input-2 | Phone input with libphonenumber-js. | [Link](https://github.com/damianricobelli/shadcn-phone-input) | 2024-06-07 |
| shadcn-phone-input | Phone input with country validation. | [Link](https://github.com/omeralpi/shadcn-phone-input) | 2024-03-27 |
| shadcn-portable-text-editor | A headless rich text editor built upon Sanity's Portable Text Editor with opinionated starting styles | | 2025-09-06 |
| shadcn-pricing-page | Responsive pricing component with toggles. | [Link](https://github.com/aymanch-03/shadcn-pricing-page) | 2024-03-08 |
| shadcn-space | Open-source shadcn/ui blocks, components, and templates built with React, Tailwind, and Base UI. | [Link](https://github.com/shadcnspace/shadcnspace) | 2026-02-27 |
| shadcn-spinner | Spinner component. | [Link](https://github.com/allipiopereira/shadcn-spinner) | 2024-12-09 |
| shadcn-stepper | Complete stepper component. | [Link](https://github.com/damianricobelli/stepperize) | 2024-04-25 |
| shadcn-studio | Open Source Registry of Shadcn components and blocks. | [Link](https://shadcnstudio.com/) | 2025-04-24 |
| shadcn-table-maker | Tool for creating dynamic tables. | [Link](https://shadcn-table-maker.vercel.app/) | 2024-12-09 |
| shadcn-table-v2 | Table with server-side features. | [Link](https://github.com/sadmann7/shadcn-table) | 2024-03-27 |
| shadcn-tanstack-form | Seamless shadcn TanStack Form component set. Fully featured with validation support and type-safety. | [Link](https://shadcn-tanstack-form.felipestanzani.com/) | 2025-10-08 |
| shadcn-timeline | Customizable timeline component. | [Link](https://github.com/timDeHof/shadcn-timeline) | 2024-06-22 |
| shadcn-timeline-2 | Alternative timeline component. | [Link](https://timeline.rilcy.app) | 2024-11-08 |
| shadcn-tiptap | Custom Tiptap editor extensions. | [Link](https://github.com/NiazMorshed2007/shadcn-tiptap) | 2024-11-08 |
| shadcn-vaults | Collection of various interactive components & blocks for Internal Tools UI like Dashboard, Monitoring, Admin, CMS, and more. Specifically made for Full-Stack Dev | [Link](https://shadcn-vaults.vercel.app/) | 2025-07-03 |
| shadcn-tree-view | Hierarchical data component. | [Link](https://github.com/mrlightful/shadcn-tree-view) | 2024-09-23 |
| shadcn-ui-blocks | Collection of responsive UI blocks. | [Link](https://shadcn-ui-blocks.vercel.app/) | 2024-06-15 |
| shadcn-ui-expansions | Additional useful components. | [Link](https://github.com/hsuanyi-chou/shadcn-ui-expansions) | 2024-06-07 |
| shadcn-ui-sidebar | Retractable responsive sidebar. | [Link](https://github.com/salimi-my/shadcn-ui-sidebar) | 2024-05-03 |
| shadcn-ui-templates | Free & Premium templates collection. | [Link](https://shadcnui-templates.com) | 2024-09-18 |
| shadcnship | Production-ready shadcn/ui component registry for building modern SaaS applications with Next.js, TypeScript, and Tailwind CSS. | [Link](https://github.com/arnaudvolp/shadcnship) | 2026-01-31 |
| shaduxe-ui | Component variants for shadcn/ui. | [Link](https://ui.shaduxe.com) | 2024-12-27 |
| shadcn-event-calendar | A beautiful and flexible event calendar component inspired by Google Calendar and Notion, built with Shadcn UI, TailwindCSS, and Framer Motion. | [Link](https://shadcn-event-calendar.vercel.app) | 2025-07-16 |
| shsfui | Motion-first React components built with Tailwind CSS + Framer Motion. | [Link](https://www.shsfui.com) | 2025-03-12 |
| shuip | Provides ready-to-use, business-focused components that help you ship faster. | [Link](https://shuip.plvo.dev/docs) | 2025-10-09 |
| siddz-ui | A curated collection of modern, reusable React components. Built with performance and accessibility in mind. Copy, paste, and customize. | [Link](https://siddz.com/components) | 2026-02-13 |
| simple-ai | Components and blocks to easily build AI apps | [Link](https://simple-ai.dev) | 2025-01-21 |
| simple-image-uploader | Image uploader with dnd, validation and previews | [Link](https://simple-image-uploader-bice.vercel.app/) | 2025-03-25 |
| simplekit | Wallet and account component for Wagmi. | [Link](https://github.com/vaunblu/SimpleKit) | 2024-09-18 |
| skiper-ui | Stand out from others with this crazzy ui library built with shad-cn cli | [Link](https://skiper-ui.com/) | 2025-02-03 |
| solanauth | Solana wallet authentication modal. | [Link](https://solanauth.vercel.app/) | 2024-11-25 |
| sortable | Sortable component with dnd-kit. | [Link](https://github.com/sadmann7/sortable) | 2024-04-09 |
| spectrum-ui | Collection using Aceternity UI Magic UI. | [Link](https://github.com/arihantcodes/spectrum-ui) | 2024-11-25 |
| stateful-button | A shadcn/ui button component that provides clear visual feedback with full accessibility support for loading/progress, success, and error states during asynchronous operations. | [Link](https://github.com/nanyx95/Stateful-Button-React) | 2025-09-06 |
| stocks | Stock Picker with Next.js charts. | [Link](https://github.com/aryanvichare/stocks) | 2024-07-07 |
| supabase-shadcn-database-example | supabase + shadcn/ui datatable | [Link](https://github.com/thisisfel1x/supabase-shadcn-database-example) | 2024-12-30 |
| supercharged-shadcn-components | Type-safe form components collection. | [Link](https://github.com/slickwit/supercharged-shadcn-components) | 2024-11-25 |
| svelte-image-uploader | Svelte image uploader with dnd, validation and previews. | [Link](https://svelte-image-uploader.vercel.app/) | 2025-06-10 |
| trable-craft | Drizzle ORM-powered table engine that auto-generates tables from your schema. Built on TanStack Table with server-side filtering, sorting, pagination, search, export, and URL state sync. | [Link](https://github.com/jacksonkasi1/TableCraft) | 2026-02-27 |
| tanstack-ui-table | Customizable table with @tanstack/table and shadcn/ui | [Link](https://github.com/drefahl/tanstack-ui-table) | 2025-02-27 |
| the-gridcn | Tron inspired shadcn/ui theme | [Link](https://thegridcn.com/) | 2026-02-06 |
| time-picker | Simple TimePicker component. | [Link](https://github.com/openstatusHQ/time-picker) | 2024-04-29 |
| tnks-data-table | Advanced data table component built with shadcn/ui and TanStack Table featuring server-side operations, row selection, filtering, column customization, and export functionality. Fully TypeScript compatible with comprehensive documentation. | [Link](https://github.com/jacksonkasi1/tnks-data-table) | 2025-04-19 |
| tool-ui | Beautiful, ready-to-use UI components for AI tool calls and MCP UI. | [Link](https://www.tool-ui.com/) | 2025-12-10 |
| tour | A component for building onboarding tours. Designed to integrate with shadcn/ui. | [Link](https://onboarding-tour.vercel.app) | 2025-12-04 |
| tremor | Components for charts and dashboards. | [Link](https://github.com/tremorlabs/tremor) | 2024-06-15 |
| twblocks | Website blocks based on shadcn & Radix. | [Link](https://github.com/tommyjepsen/twblocks) | 2024-11-25 |
| tweet-to-code | Twitter design recreations as code. | [Link](https://tweet-to-code.vercel.app/) | 2024-12-26 |
| ui-beats | Animated React Components collection. | [Link](https://uibeats.com) | 2024-09-23 |
| ui-layouts | UI Layouts isn’t just a library. It’s a complete toolkit with components, effects, design tools, and ready-to-use blocks, everything you need to build modern interfaces, faster. | [Link](https://www.ui-layouts.com/) | 2025-10-15 |
| ui-nference-sh | a shadcn registry of react ui components for building ai-powered applications, chatbots, and ai agent interfaces. | [Link](https://ui.inference.sh/) | 2026-02-13 |
| uixmat-onborda | Product tour for Next.js applications. | [Link](https://github.com/uixmat/onborda) | 2024-06-07 |
| uselayouts | Free premium animated React components and micro-interactions built with Framer Motion and Tailwind CSS. Modern, ready-to-use motion components for high-converting websites. | [Link](https://uselayouts.com) | 2026-01-23 |
| vaul | Drawer component for React. | [Link](https://vaul.emilkowal.ski/) | 2024-06-07 |
| vengence-ui | A modern, animated UI component library designed to help developers build beautiful landing pages and interfaces faster. | [Link](https://www.vengenceui.com/) | 2026-02-17 |
| vyoma-ui | Beautiful Components Made on top of Shadcn/ui with Spatial Wisdom Inside. Truly Beyond UI. | [Link](https://vyomaui.design/) | 2025-07-15 |
| warcraftcn | A set of components inspired by classic Warcraft III RTS UI aesthetics | [Link](https://www.warcraftcn.com/) | 2026-02-06 |
| wds registry | A collection of components that you can use to build your own component library. | [Link](https://wds-shadcn-registry.netlify.app) | 2025-09-06 |
| zoom-charts | Zoomable Charts with shadcn/ui. | [Link](https://github.com/shelwinsunga/zoom-chart-demo) | 2024-07-16 |
## Registries
| Name | Description | Link | Date |
| ------------------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ | ---------- |
| efferd | ready-to-use shadcn blocks that just work — modern, responsive, and built for speed. | [Link](http://efferd.com/) | 2026-03-05 |
| more-shadcn | A collection of high-quality, copy-paste components for Svelte 5, built on top of shadcn-svelte. | [Link](https://more-shadcn.noair.fun/) | 2026-01-23 |
| neobrutalism-vue | A vue-based registry of neobrutalism-styled Tailwind components. | [Link](https://github.com/michaelsieminski/neobrutalism-vue) | 2025-12-04 |
| registry.directory | A curated directory to discover, preview, and copy shadcn/ui registries. | [Link](https://github.com/rbadillap/registry.directory) | 2025-09-20 |
| tailark | Shadcn blocks for building modern marketing websites | [Link](https://tailark.com) | 2026-02-06 |
| undraw-cn | Beautiful, customizable React components for unDraw illustrations. | [Link](https://undraw-cn.vaatun.com) | 2025-12-04 |
## Plugins and Extensions
| Name | Description | Link | Date |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | ---------- |
| chat-with-youtube | A chrome extension is designed to give you the ability to efficiently summarize videos, easily search for specific parts, and enjoy additional useful features. | [Link](https://chat-with-youtube.vercel.app/) | 2024-06-07 |
| designgui | A Chrome Browser Extension for managing colors in CSS Variables. | [Link](https://www.designgui.io/) | 2024-09-05 |
| imprompt | A Chrome extension that enhances prompts on AI websites directly, making your AI prompts more effective and productive. | [Link](https://github.com/avalynndev/imprompt) | 2025-09-06 |
| raycast-shadcn | Raycast extension to Browse shadcn/ui documentation, components, and examples. | [Link](https://www.raycast.com/luisFilipePT/shadcn-ui) | 2024-06-05 |
| shadcn-hsl-preview | shadcn HSL Preview extension for Visual Studio Code. | [Link](https://marketplace.visualstudio.com/items?itemName=dexxiez.shadcn-color-preview) | 2024-09-05 |
| shadcn-ui | Add components from shadcn/ui directly from VS Code. | [Link](https://marketplace.visualstudio.com/items?itemName=SuhelMakkad.shadcn-ui) | 2024-03-08 |
| shadcn/ui Components Manager | A plugin for Jetbrain products. It allows you to manage your shadcn/ui components across Svelte, React, Vue, and Solid frameworks with this plugin. Simplify tasks like adding, removing, and updating components. | [Link](https://plugins.jetbrains.com/plugin/23479-shadcn-ui-components-manager) | 2024-03-28 |
| vscode-shadcn-svelte | VS Code extension for shadcn/ui components in Svelte projects. | [Link](https://marketplace.visualstudio.com/items?itemName=Selemondev.vscode-shadcn-svelte&ssr=false#overview) | 2024-03-28 |
| vscode-shadcn-ui-snippets | Easily import and use shadcn-ui components with ease using snippets within VSCode. Just type cn or shadcn in your jsx/tsx file and you will get a list of all the components to choose from. | [Link](https://marketplace.visualstudio.com/items?itemName=VeroXyle.shadcn-ui-snippets) | 2024-06-05 |
| vscode-shadcn-vue | Extension for integrating shadcn/ui components into Vue.js projects. | [Link](https://marketplace.visualstudio.com/items?itemName=Selemondev.shadcn-vue) | 2024-03-28 |
## Colors and Customizations
| Name | Description | Link | Date |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------- |
| 10000+Themes for shadcn/ui | 10000+ Themes for shadcn/ui. | [Link](https://ui.jln.dev/) | 2024-02-22 |
| dizzy | Bootstrap a new Next or Vite project with shadcn/ui. Customize font, icons, colors, spacing, radii, and shadows. | [Link](https://dizzy.systems) | 2024-06-05 |
| ewgenius/ui | Create custom themes for shadcn/ui effortlessly using vibrant palettes from Radix Colors. | [Link](https://ui.ewgenius.me/shadcn-radix-colors) | 2024-10-11 |
| gradient-picker | Fancy Gradient Picker built with shadcn/ui, Radix UI, and Tailwind CSS. | [Link](https://github.com/Illyism/gradient-picker) | 2024-03-08 |
| navnote/rangeen | Tool that helps you to create a colour palette for your website. | [Link](https://github.com/navnote/rangeen) | 2024-06-05 |
| shadesigner.com | A shadcn/ui Palette Generator & Theme Designer with a beautiful interface. | [Link](https://shadesigner.com) | 2024-12-26 |
| shadcn-ui-customizer | POC - shadcn/ui themes with color pickers. | [Link](https://github.com/Railly/shadcn-ui-customizer) | 2024-03-08 |
| shadcn theme editor | Shadcn Theme Editor is a user-friendly component designed to simplify the process of managing and customizing theme colors in Shadcn-based projects. | [Link](https://github.com/programming-with-ia/shadcn-theme-editor/) | 2024-08-19 |
| smui | Nord-inspired terminal theme for shadcn/ui with monospace typography, zero border radius, and frost-blue accents. | [Link](https://smui.statico.io) | 2026-02-18 |
| tweakcn | powerful theme editor for shadcn/ui components, offering beautifully designed themes and seamless Tailwind CSS V4 integration | [Link](https://tweakcn.com/) | 2025-03-25 |
| ui-colorgen | An application designed to assist you with color configuration of shadcn/ui. | [Link](https://ui-colorgen.vercel.app/) | 2024-02-22 |
| zippy starter's shadcn/ui theme generator | Easily create custom themes from a single colour that you can copy and paste into your apps. | [Link](https://zippystarter.com/tools/shadcn-ui-theme-generator) | 2024-03-13 |
## Animations
| Name | Description | Link | Date |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ---------- |
| animata | Hand-crafted ✍️ interaction animations and effects from around the internet 🛜 to copy and paste into your project. | [Link](https://animata.design) | 2024-08-26 |
| animate-ui | A fully animated, open-source React component distribution. Browse a list of animated primitives, components and icons you can install and use in your projects. | [Link](https://animate-ui.com/) | 2025-10-15 |
| magicui.design | Largest collection of open-source react components to build beautiful landing pages. | [Link](https://magicui.design) | 2024-04-25 |
| motionvariants | Beautiful Framer Motion Animations. | [Link](https://github.com/chrisabdo/motionvariants) | 2024-03-08 |
| smooth-ui | Highly customizable, production-ready UI blocks for building beautiful websites and apps that look and feel the way you mean it. | [Link](https://smoothui.dev/) | 2025-10-15 |
| tailwindcss-motion | A new simple syntax animation library. Batteries included. Infinitely configurable. | [Link](https://rombo.co/tailwind/) | 2024-11-13 |
## Tools
| Name | Description | Link | Date |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ---------- |
| 5devs | A website to get fake Brazilian data for testing purposes. | [Link](https://www.5devs.com.br/) | 2024-05-31 |
| bento-hub | BentoHub is an application where you can create a bento grid for your GitHub profile readme. | [Link](https://github.com/amittam104/BentoHub) | 2024-09-09 |
| cheatsheet | A comprehensive, interactive reference for shadcn/ui components with live previews, code examples, and instant copy functionality. | [Link](https://shadcnstore.com/cheatsheet/) | 2025-09-22 |
| cut-it | Link shortener built using Next.js App Router, Server Actions, Drizzle ORM, Turso, and styled with shadcn/ui. | [Link](https://github.com/mehrabmp/cut-it) | 2024-06-07 |
| country-data-in-charts | Globe Graph is a web app that visualizes countries' data like GDP, GDP per capita, and population in different years using many charts. | [Link](https://globe-graph.vercel.app/) | 2024-09-09 |
| dev-quotes | A website that displays quotes from professional programmers. | [Link](https://dev-quotes-six.vercel.app/) | 2025-01-07 |
| down-dev-detector | This app lists all the services currently down and uses Atlassian Status Page and others (soon). | [Link](https://github.com/birobirobiro/downdevdetector) | 2024-11-04 |
| cv-forge | Resume builder built with @shadcn/ui, react-hook-form, and react-pdf. | [Link](https://cvforge.app) | 2024-11-04 |
| focus-brew | A free productivity toolkit that combines essential tools to help you stay focused, organized, and efficient throughout your workday. | [Link](https://focusbrew.vercel.app) | 2025-05-14 |
| form-builder | UI-based codegen tool to easily create beautiful and type-safe @shadcn/ui forms. | [Link](https://github.com/AlandSleman/FormBuilder) | 2024-06-07 |
| form-builder-fast | Shadcn Form Builder - Build forms in minutes for free. | [Link](https://ui.indie-starter.dev/form-builder) | 2024-12-26 |
| graphitup | Create free downloadable Shadcn-themed chart images. Supports PNG, JPEG, WEBP, and even WEBM videos. Upload your own data for more realistic designs. | [Link](https://graphitup.com/tools) | 2025-12-26 |
| hook-again | A collection of shadcn/ui installable React Hooks. | [Link](https://github.com/ilyichv/hookagain) | 2024-11-04 |
| Img2m3 | A developer tool that automates the process of creating cohesive, accessible design systems. It bridges the gap between Google's Material Design 3 algorithms and the modern Tailwind CSS v4 ecosystem. | [Link](https://img2m3.vercel.app/studio) | 2026-01-23 |
| imgsrc | Generate beautiful Open Graph images with zero effort. | [Link](https://imgsrc.io/) | 2024-05-31 |
| invoify | An invoice generator app built using Next.js, TypeScript, and shadcn/ui. | [Link](https://github.com/aliabb01/invoify) | 2024-03-27 |
| jobsync | JobSync is a job seekers' assistant to manage job search efficiently. | [Link](https://github.com/Gsync/jobsync) | 2024-07-17 |
| memfree | Open-source hybrid AI search engine, instantly get accurate answers from the internet, bookmarks, notes, and docs. Built using Next.js and shadcn/ui. | [Link](https://github.com/memfreeme/memfree) | 2024-07-22 |
| opensearch-ai | SearchGPT/Perplexity clone but personalized for you. | [Link](https://github.com/supermemoryai/opensearch-ai) | 2024-12-26 |
| pagegen.ai | An AI Page Generator with Claude AI, React, and shadcn/ui. Generate web pages from text, screenshots, and templates with one click. | [Link](https://pagegen.ai) | 2024-12-26 |
| pastecode | Pastebin alternative built with TypeScript, Next.js, Drizzle, shadcn/ui, and RSC. | [Link](https://github.com/Quorin/PasteCode.app) | 2024-06-07 |
| pictera | Generate Open Graph images without design skills. | [Link](https://pictera.co) | 2025-10-08 |
| proxmox-helper-scripts | A catalog of scripts for your Proxmox VE homelab, built with the Next.js App Router and styled with shadcn/ui. | [Link](https://github.com/BramSuurdje/proxmox-helper-scripts) | 2024-07-16 |
| quack-db | Open-source in-browser DuckDB SQL editor. | [Link](https://github.com/mattf96s/QuackDB) | 2024-10-03 |
| shadcn-easy-install | Install all shadcn components easily. One-click to install all selected components. | [Link](https://shadcn-easy-install.vercel.app/) | 2025-06-10 |
| shadcn-form-builder | Create forms with Shadcn, react-hook-form, and Zod within minutes. | [Link](https://shadcn-form-build.vercel.app/) | 2024-10-03 |
| shadcn-hooks | A comprehensive React Hooks Collection built with Shadcn. | [Link](https://shadcn-hooks.com/) | 2025-10-13 |
| shadcn-play | A playground for building and previewing shadcn/ui components with a live editor. | [Link](https://github.com/ephraimduncan/shadcn-play) | 2026-02-13 |
| shadcn-pricing-page-generator | The easiest way to get a React pricing page with shadcn/ui, Radix UI, and/or Tailwind CSS. | [Link](https://shipixen.com/shadcn-pricing-page) | 2024-03-08 |
| shadcn-theme-editor | Shadcn Theme Editor is a user-friendly component designed to simplify the process of managing and customizing theme colors in Shadcn-based projects. | [Link](https://shadcnthemeeditor.vercel.app) | 2024-08-19 |
| shadcn-zod-form | CLI tool to generate shadcn/ui forms from Zod schemas. | [Link](https://github.com/ilyichv/shadcn-zod-form) | 2024-10-03 |
| sharable-form-builder | A sharable form builder for creating forms and sharing your form link, based on shadcn/ui and Next.js. | [Link](https://github.com/ayoubben18/sharable-form-builder) | 2024-10-12 |
| shoogle | A shadcn search engine | [Link](https://shoogle.dev/) | 2026-02-13 |
| slidytabs | A tool that adds a sliding indicator animation to shadcn `<Tabs />` without changing how you use or customize the component | [Link](https://slidytabs.dev) | 2026-01-23 |
| someday | Free to host and open-source Cal.com/Calendly alternative built on Google Apps Script for Gmail users. | [Link](https://github.com/rbbydotdev/someday) | 2024-12-26 |
| sweep | Sweep is a modern, open-source gradient generator built for designers and developers. Create beautiful linear and radial gradients with real-time preview, noise/blur effects, and export to CSS, Tailwind, SVG, or JPG. Free forever. No sign-up required. | [Link](https://github.com/Johuniq/sweep) | 2025-11-11 |
| tancn | Build powerful forms and tables with ease using TanStack technologies | [Link](https://tancn.dev/) | 2025-11-02 |
| tinte | An opinionated VS Code Theme Generator 🎨. | [Link](https://tinte.railly.dev/) | 2024-10-03 |
| translate-app | Translate App using TypeScript, Tailwind CSS, NextJS, Bun, shadcn/ui, AI SDK/OpenAI, and Zod. | [Link](https://github.com/developaul/translate-app) | 2024-07-08 |
| typelabs | MonkeyType-inspired typing test app built with React, shadcn, and Zustand at its core. | [Link](https://github.com/imsandeshpandey/typelabs) | 2024-07-08 |
| ui-builder | A React component editor that provides a no-code, visual way to create UIs, fully compatible with shadcn/ui and custom components. | [Link](https://github.com/olliethedev/ui-builder) | 2024-10-11 |
| ui-fonts | Test and preview fonts in real-time for all your design needs. Choose the perfect typeface with ease. | [Link](https://www.uifonts.app/) | 2024-10-23 |
| v0 | Vercel's generative UI system, built on shadcn/ui and TailwindCSS, allows effortless UI generation from text prompts and/or images. | [Link](https://v0.dev/) | 2024-03-27 |
| vercel-status-tracker | Track the status of all of your projects deployed via Vercel. Built with shadcn/ui and TailwindCSS. | [Link](https://vercel-status-tracker.vercel.app) | 2025-01-02 |
| wallhaven-desktop | Wallhaven Wallpaper software desktop. Create a Wallhaven API-based client, a true wallpaper software. | [Link](https://github.com/ErKeLost/wallhaven-desktop) | 2024-10-23 |
## Websites and Portfolios
| Name | Description | Link | Date |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | ---------- |
| andrewsam.xyz | A revamped version of the popular tailwind-nextjs-starter-blog using shadcn/ui, along with a resume section and experience timeline component. | [Link](https://www.andrewsam.xyz/) | 2024-09-18 |
| anishshobithps.com | Personal portfolio of a software developer, grid-styled design . | [Link](https://anishshobithps.com/) | 2026-02-27 |
| birobirobiro.dev | A personal developer portfolio. | [Link](https://birobirobiro.dev/) | 2024-07-29 |
| bucharitesh.in | A minimal portfolio with awesome craft's registry. | [Link](https://bucharitesh.in) | 2025-12-25 |
| chanhdai.com | A minimal portfolio, component registry, and blog. | [Link](https://chanhdai.com) | 2025-09-11 |
| devfolios | Find best portfolio inspiration from all over the internet | [Link](https://devfolios-one.vercel.app/) | 2025-10-25 |
| godly | Astronomically good web design inspiration. Only the best of the best. | [Link](https://godly.website/) | 2024-07-29 |
| hritu.art | A clean, modern designer portfolio blending minimal aesthetics with functional UI and built-in email support via React Email. | [Link](https://github.com/suraj-xd/design-portfolio) | 2025-06-10 |
| kinhdev24 | Developer portfolio built with Next.js, shadcn/ui, Aceternity, and Magic UI | [Link](https://kinhdev.id.vn/) | 2025-07-08 |
| list.swajp.me | It has never been easier to find the right projects or designs by inspiring successful people. | [Link](https://list.swajp.me) | 2024-07-29 |
| mpakravan | Portfolio website using React, Next.js, Tailwind CSS, ShadCN, and GSAP, modern design system. | [Link](https://mpakravan.com/en) | 2025-10-26 |
| nathans-ai | An AI Chatbot acting as a portfolio, built with shadcn/ui components. | [Link](https://chat.brodin.dev) | 2024-11-21 |
| ifte-13 | Portfolio made with Next.js, shadcn/ui (with magic ui ) and Email.js | [Link](https://ifte-13.vercel.app/) | 2025-10-25 |
| shubhporwal.me | An eye-catching developer portfolio, built on NextJS, GSAP, Tailwind, and React. | [Link](https://www.shubhporwal.me/) | 2024-10-03 |
| swajp.me | A visually appealing portfolio and resource hub. | [Link](https://swajp.me) | 2024-07-29 |
| windows-11-clone | A sleek Windows 11 clone built with React, Next.js, Tailwind CSS, ShadCN, and Framer-Motion, featuring smooth animations, draggable windows, and a modern design system. | [Link](https://win11.oimmi.com/) | 2025-02-20 |
## Platforms
| Name | Description | Link | Date |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ---------- |
| anonypost | Share your thoughts and experiences anonymously by posting on the platform. Crafted using t3-stack. | [Link](https://github.com/avalynndev/anonypost) | 2024-07-17 |
| bolhadev | The quickest path to learn English is speaking it regularly. Just find someone to chat with. | [Link](https://bolhadev.chat/) | 2024-06-04 |
| citeme | AI-powered academic citation generator. Searches 11+ databases and formats in 40+ styles (APA, ABNT, MLA, etc.). Web app, Chrome extension, Google Docs add-on, and Word add-in. | [Link](https://citeme.app) | 2026-03-03 |
| enjoytown | A free anime, manga, movie, and TV-shows streaming platform. Built with Next.js, shadcn/ui. | [Link](https://github.com/avalynndev/enjoytown) | 2024-06-04 |
| grade-calculator | A grade calculator/dashboard for students, aiming to provide a better overview of academic performance. | [Link](https://grades.nstr.dev/) | 2024-12-27 |
| infinitunes | A simple music player web app built using Next.js, shadcn/ui, Tailwind CSS, Drizzle ORM, and more. | [Link](https://github.com/rajput-hemant/infinitunes) | 2024-06-04 |
| kd | Ad-free Kdrama streaming app. Built with Next.js, Drizzle ORM, NeonDB, and shadcn/ui. | [Link](https://github.com/gneiru/kd) | 2024-05-31 |
| memergez | Quickly generate memes by entering text or an avatar URL, with support for many meme commands. | [Link](https://github.com/avalynndev/memergez) | 2024-08-26 |
| midday-components | A collection of open-source components based on Midday features. | [Link](https://midday.ai/components) | 2024-11-21 |
| multiboard | Minimal Kanban platform. Built with Better-Auth, Next.js, ZenStack, Prisma, and shadcn/ui. | [Link](https://github.com/olliethedev/multiboard) | 2025-07-15 |
| openhive | Open-source, self-hosted Slack alternative with channels, DMs, threads, reactions, file uploads, and video/audio calls. Built with Next.js, Supabase, Zustand, and shadcn/ui. | [Link](https://github.com/arseneHuot/openhive) | 2026-03-14 |
| plotwist | Easy management and reviews of your movies, series, and animes using Next.js, Tailwind CSS, Supabase, and shadcn/ui. | [Link](https://plotwist.app/en-US) | 2024-05-31 |
| snapimg | Fast, privacy-focused image compression tool supporting PNG, JPEG, WebP, AVIF. Built with React 19, Vite, Tailwind CSS, and shadcn/ui. | [Link](https://github.com/Moresl/snapimg) | 2025-12-15 |
| veritas-kanban | Self-hosted Kanban board with AI agent integration, time tracking, and 1,255 tests. Built with React 19, Vite 6, TanStack Query, dnd-kit, and shadcn/ui. | [Link](https://github.com/BradGroux/veritas-kanban) | 2026-01-29 |
| xeramail | Temporary email address service built with Next.js and shadcn/ui, offering fast inbox access, modern UI, and better control over disposable emails. | [Link](https://xeramail.com) | 2026-01-01 |
| youropinion.is | Free survey platform which supports importing your exisitng shadcn/ui theme | [Link](https://youropinion.is/news/202505-match-my-style) | 2025-06-16 |
## Ports
| Name | Description | Link | Date |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ---------- |
| Angular | Angular port of shadcn/ui. | [Link](https://github.com/goetzrobin/spartan) | 2024-03-21 |
| Basecoat | Vanilla HTML, CSS and JS port of shadcn/ui. | [Link](https://basecoatui.com) | 2025-07-07 |
| Flutter | Flutter port of shadcn/ui. | [Link](https://github.com/nank1ro/shadcn-ui) | 2024-06-07 |
| Franken UI | HTML-first, framework-agnostic, beautifully designed components that you can truly copy and paste into your site. Accessible. Customizable. Open Source. | [Link](https://www.franken-ui.dev/) | 2024-06-07 |
| JollyUI | shadcn/ui compatible react aria components. | [Link](https://github.com/jolbol1/jolly-ui) | 2024-06-07 |
| Kotlin | Kotlin port of shadcn/ui. | [Link](https://github.com/dead8309/shadcn-kotlin) | 2024-06-07 |
| mkdocs-shadcn | MkDocs port of shadcn/ui. | [Link](https://github.com/asiffer/mkdocs-shadcn) | 2025-07-07 |
| .NET (ShadUI) | Avalonia port of shadcn/ui. Based on SukiUI | [Link](https://github.com/accntech/shad-ui/) | 2024-02-22 |
| Phoenix Liveview | Phoenix Liveview port of shadcn/ui. | [Link](https://github.com/bluzky/salad_ui) | 2024-06-07 |
| React Native | React Native port of shadcn/ui. | [Link](https://github.com/Mobilecn-UI/nativecn-ui) | 2024-06-07 |
| React Native (recommended) | React Native port of shadcn/ui (recommended). | [Link](https://github.com/mrzachnugent/react-native-reusables) | 2024-12-27 |
| Ruby | Ruby port of shadcn/ui. | [Link](https://github.com/aviflombaum/shadcn-rails) | 2024-06-07 |
| Solid | Solid port of shadcn/ui. | [Link](https://github.com/hngngn/shadcn-solid) | 2024-03-28 |
| Svelte | Svelte port of shadcn/ui. | [Link](https://github.com/huntabyte/shadcn-svelte) | 2024-02-22 |
| Swift | Swift port of shadcn/ui. | [Link](https://github.com/Mobilecn-UI/swiftcn-ui) | 2024-06-07 |
| Sysinfocus simple/ui | Razor component library for Blazor, inspired by shadcn/ui. | [Link](https://sysinfocus.github.io/shadcn-inspired/) | 2024-09-09 |
| Vue | Vue port of shadcn/ui. | [Link](https://github.com/radix-vue/shadcn-vue) | 2024-02-22 |
## Design System
| Name | Description | Link | Date |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------- |
| shadcn-storybook-registry | Registry of stories for the shadcn components. Quickly get the atomic level components documented in Storybook. | [Link](https://registry.lloydrichards.dev/) | 2025-02-07 |
| obra-shadcn-ui | This file replicates all 51 shadcn/ui v4 components in a composable way as a reusable Figma library.. | [Link](https://www.figma.com/community/file/1514746685758799870/obra-shadcn-ui) | 2025-06-16 |
| shadcn-ui-components | Every component recreated in Figma. | [Link](https://www.figma.com/community/file/1342715840824755935/shadcn-ui-components) | 2024-03-21 |
| shadcn-ui-storybook (JheanAntunes) | All shadcn/ui components registered in the storybook by JheanAntunes. | [Link](https://65711ecf32bae758b457ae34-uryqbzvojc.chromatic.com/) | 2024-12-27 |
| shadcn-ui-storybook (fellipeutaka) | All shadcn/ui components registered in the storybook by fellipeutaka. | [Link](https://fellipeutaka-ui.vercel.app/?path=/docs/components-accordion--docs) | 2024-12-27 |
## Boilerplates / Templates
| Name | Description | Link | Date |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| agentic-react-nextjs-shadcn | Agent-testable SaaS starter built with Next.js 16, shadcn/ui, and Tailwind CSS. Includes accessibility-first components, semantic HTML for AI agent testing, and production-ready patterns. | [Link](https://github.com/iscale-llc/agentic-react-nextjs-shadcn) | 2026-03-03 |
| astro-erudite | An opinionated, unstyled static blogging template—built with Astro, Tailwind, and shadcn/ui. | [Link](https://github.com/jktrn/astro-erudite) | 2025-10-25 |
| atomic-crm | Open-source React CRM built on top of Supabase [Demo site](https://marmelab.com/atomic-crm-demo) | [Link](https://github.com/marmelab/atomic-crm) | 2025-09-16 |
| autoflow | An open source GraphRAG (Knowledge Graph) built on top of TiDB Vector, LlamaIndex, and DSPy. [Demo site](https://tidb.ai). | [Link](https://github.com/pingcap/autoflow) | 2024-12-06 |
| browser-extension-starter-plasmo-shadcn-trpc | Browser extension starter kit featuring Plasmo, React, Shadcn, and tRPC. | [Link](https://github.com/poweroutlet2/browser-extension-starter-plasmo-shadcn-trpc) | 2024-10-29 |
| chadnext | Quick Starter Template includes Next.js 14 App Router, shadcn/ui, LuciaAuth, Prisma, Server Actions, Stripe, Internationalization, and more. | [Link](https://github.com/moinulmoin/chadnext) | 2024-04-25 |
| cloudflare-saas-stack | An opinionated, batteries-included starter kit for quickly building and deploying SaaS products on Cloudflare. | [Link](https://github.com/Dhravya/cloudflare-saas-stack) | 2024-07-24 |
| create-tauri-core | A project template for creating a Tauri app with Vite, React, and Tailwind CSS. | [Link](https://github.com/mrlightful/create-tauri-core) | 2024-09-23 |
| design-system-template | Turborepo + TailwindCSS + Storybook + shadcn/ui. | [Link](https://github.com/arevalolance/design-system-template) | 2024-06-06 |
| devstarter | Devstarter is a bold, one-page developer portfolio template with a distinctive cyberpunk aesthetic. Built with Next.js, shadcn/ui, and Tailwind CSS, it's designed to help developers showcase their work, skills, and personality in a way that stands out. | [Link](https://github.com/zippystarter/template-devstarter) | 2026-01-27 |
| easy-ui | 50+ High Quality Open Source Website Templates built using NextJS + shadcn/ui + Tailwind CSS + Framer Motion and more. | [Link](https://github.com/DarkInventor/easy-ui) | 2024-08-06 |
| ecommerce-kit | Next.js starter kit with the tools you need to quickly launch your e-commerce site. | [Link](https://ecommercekit.dev) | 2025-09-06 |
| electron-shadcn | Electron app template with shadcn/ui and a bunch of other libs and tools ready to use. | [Link](https://github.com/LuanRoger/electron-shadcn) | 2024-06-17 |
| forjnot | Modern Project Starter Kit | Launch your next project faster with Forjnot - A professional, customizable and clean starting point featuring modern tech stack and best practices | 2025-11-20 |
| full-stack-monorepo-starter | Full stack monorepo template built using shadcn/ui + Fastify + graphql + vitejs + Docker and more. | [Link](https://github.com/mnove/monorepo-starter-graphql) | 2025-06-10 |
| fumadocs-starter | A fully-fledged Fumadocs starter template with built-in plugins, AI features, and everything you need to build your next docs site. | [Link](https://github.com/techwithanirudh/fumadocs-starter) | 2025-11-20 |
| horizon-ai-nextjs-shadcn-boilerplate | Premium AI NextJS & shadcn/ui Boilerplate + Stripe + Supabase + OAuth. | [Link](https://horizon-ui.com/boilerplate-shadcn) | 2024-05-24 |
| kirimase | A template and boilerplate for quickly starting your next project with shadcn/ui, Tailwind CSS, and Next.js. | [Link](https://kirimase.dev/) | 2024-06-11 |
| login-auth | A login authentication web app built with Vite + React, Tailwind CSS, and Shadcn UI. It uses Firebase for Google sign-in, email sign-up, and password reset. | [Link](https://shadcn-login-auth.vercel.app/) | 2026-03-15 |
| magicui-startup-templates | Magic UI Startup template built using shadcn/ui + TailwindCSS + Framer Motion. | [Link](https://magicui.design/docs/templates/startup) | 2024-04-25 |
| next-shadcn-admin-dashboard | Modern Admin Dashboard Template built with Shadcn UI and Next.js 15 | [Link](https://github.com/arhamkhnz/next-shadcn-admin-dashboard) | 2025-10-25 |
| nextMotion | Webdev portfolio template with Nodemailer integrated for easy contact form setup. Uses shadcn/ui + TailwindCSS + Framer Motion. | [Link](https://github.com/yoyocharlie/nextMotion) | 2024-09-23 |
| next-js-boilerplate | Quickly set up a Next.js project with TypeScript, NextAuth.js, PostgreSQL (Prisma), Sentry, Tailwind CSS v4, shadcn/ui, Zod, Zustand, nuqs, ESLint, Husky, and Prettier. [Demo site](https://next-js-boilerplate-sage-nine.vercel.app/). | [Link](https://github.com/AbhishekSharma55/next-js-boilerplate) | 2025-09-08 |
| next-shadcn-dashboard-starter | Admin Dashboard Starter with Nextjs 14 and shadcn/ui. | [Link](https://github.com/Kiranism/next-shadcn-dashboard-starter) | 2024-06-06 |
| next-starter | A Next.js starter template packed with features like TypeScript, TailwindCSS, Next-auth, Eslint, Stripe, testing tools, and more. Jumpstart your project with efficiency and style. | [Link](https://github.com/Skolaczk/next-starter) | 2024-09-23 |
| nextjs-mdx-blog | Starter template built with Contentlayer, MDX, shadcn/ui, and Tailwind CSS. | [Link](https://github.com/ChangoMan/nextjs-mdx-blog) | 2024-04-25 |
| next-js-views-template | An open-source collection of reusable view components like Calendar, Table, etc., built with Next.js and ShadCN. Easily copy and paste these pre-built UI elements into your project for fast, responsive, and customizable layouts. | [Link](https://next-js-views-template.vercel.app) | 2024-11-21 |
| next-wp | Headless Wordpress Starter built with the NextJS App Router and React Server Components. | [Link](https://github.com/9d8dev/next-wp) | 2024-11-21 |
| onyx | Full stack, batteries-included MVP Template with NextJS 14, Supabase SSR Auth & Postgres DB with CRUD operations, RBAC, Tanstack React Query, Zod Validation, MDX components, Resend, and more. | [Link](https://github.com/rmourey26/onyx) | 2024-08-13 |
| opendocs | Beautifully designed template that you can use for your projects for free. Accessible. Customizable. Open Source with i18n support. | [Link](https://opendocs.daltonmenezes.com/) | 2024-07-29 |
| react-starter-kit | An opinionated, full-stack boilerplate for building modern web apps on the edge. Features Bun, React 19, tRPC, Drizzle ORM, and Cloudflare Workers. | [Link](https://github.com/kriasoft/react-starter-kit) | 2025-09-06 |
| react-vite-starter | React starter powered with Vite + Redux Toolkit + RTKQuery + React Router + shadcn UI and many more. | [Link](https://github.com/tejachundru/react-vite-starter) | 2024-12-02 |
| shadcn-admin | A admin dashboard template for Next.js, React, Vite and Vue.js, built with Tailwind CSS. | [Link](https://shadcnadmin.com) | 2025-12-12 |
| shadcn-landing-page | Landing page template using shadcn/ui, React, TypeScript, and Tailwind CSS. | [Link](https://github.com/leoMirandaa/shadcn-landing-page) | 2024-06-06 |
| shadcn-landing-page (Vue) | Project conversion [shadcn-vue-landing-page](https://github.com/leoMirandaa/shadcn-vue-landing-page) to Next.js - Landing page template using Nestjs, shadcn/ui, TypeScript, and Tailwind CSS. | [Link](https://github.com/nobruf/shadcn-landing-page) | 2024-12-27 |
| shadcn-nextjs-free-boilerplate | Free & Open-source NextJS Boilerplate + ChatGPT API Dashboard Template. | [Link](https://github.com/horizon-ui/shadcn-nextjs-boilerplate) | 2024-05-24 |
| shadcn-nextjs-dashboard | Admin Dashboard UI built with Shadcn and NextJS. Free and Open-source. | [Link](https://github.com/NaveenDA/shadcn-nextjs-dashboard) | 2025-06-22 |
| shadcn-portfolio | A portfolio template, which uses shadcn-ui and Next.JS. | [Link](https://github.com/techwithanirudh/shadcn-portfolio) | 2025-11-20 |
| shadcn-registry-template | Template repository for building a custom component registry for shadcn/ui. | [Link](https://github.com/vantezzen/shadcn-registry-template) | 2024-09-05 |
| shadcn-saas-landing | A full-fledged SaaS Landing template built using Next.JS, shadcn/ui, and fumadocs. | [Link](https://github.com/techwithanirudh/shadcn-saas-landing) | 2025-11-20 |
| shadcn-ui-dashboard | Multipurpose and powerful admin dashboard template compatible with shadcn/ui. | [Link](https://shadcnuidashboard.com) | 2025-09-21 |
| shadcn-vue-landing-page | Landing page template using Vue, shadcn-vue, TypeScript, and Tailwind CSS. | [Link](https://github.com/leoMirandaa/shadcn-vue-landing-page) | 2024-06-06 |
| shadcn-next-workflows | Interactive workflow builder using React Flows, Next.js, and Shadcn/ui. Create, connect, and validate custom nodes easily. | [Link](https://github.com/nobruf/shadcn-next-workflows) | 2024-10-29 |
| supa-next-shad-auth | A fully responsive, fully type-safe, secure server actions, user-friendly customizable UI with best practices. Tech used: NextJS + Supabase + TypeScript + Server Actions + Zod + shadcn/ui. | [Link](https://github.com/Sahil-Sharma-23/supa-next-shad-auth) | 2024-07-02 |
| sveltekit-shadcn-starter-kit | Production ready open-source generic app template featuring database abstraction (Drizzle & Postgres), server utilities, tests, authentication (better-auth), i18n + RTL/LTR support, mdsvex, predefined pages and content (policies, legal, etc.), App Shell component, base and custom components built with shadcn/ui, Cookies management (compliance), SEO managemnt support, CLI tooling and more. [Demo](https://ssv5.templates.guylahav.com) | [Link](https://github.com/GantonL/templates/tree/main/sveltekit-shadcn-v5) | 2025-09-23 |
| t3-app-template | Admin template for T3 Stack and shadcn/ui. | [Link](https://github.com/gaofubin/t3-app-template) | 2024-04-25 |
| tailwind-admin | Open Source Shadcn Dashboard Template Built On React and Tailwind CSS | [Link](https://github.com/Tailwind-Admin/free-tailwind-admin-dashboard-template) | 2026-01-03 |
| taxonomy | An open-source application built using the new router, server components, and everything new in Next.js. | [Link](https://github.com/shadcn/taxonomy) | 2024-03-21 |
| template-next | A clean Next.js template with TypeScript, TailwindCSS, Shadcn/ui, and Prettier. | [Link](https://template-next-official.vercel.app/) | 2024-09-24 |
| turborepo-nextjs-wxt-shadcn-boilerplate | Turborepo boilerplate featuring web and web-extension apps with shadcn/ui for shared ui components and unified Typescript, ESLint, Tailwind CSS, and Prettier configs. | [Link](https://github.com/Aniket-508/turborepo-nextjs-wxt-shadcn-boilerplate) | 2025-04-24 |
| turborepo-shadcn-ui-tailwindcss | Turborepo starter with shadcn/ui & TailwindCSS pre-configured for shared UI components. | [Link](https://github.com/henriqpohl/turborepo-shadcn-ui-tailwindcss) | 2024-06-06 |
| turborepo-launchpad | A comprehensive monorepo boilerplate for shadcn projects using Turbo. It features a highly scalable setup ideal for developing complex applications with shared components and utilities. | [Link](https://github.com/JadRizk/turborepo-launchpad) | 2024-06-10 |
| waitly | A simple and useful waitlist Next.js and Shadcn UI template. | [Link](https://shadcnuikit.com/template/waitly-free-waitlist-template) | 2025-12-19 |
| wordpress-plugin-boilerplate | WordPress Plugin Boilerplate utilizing modern web technologies and tools such as React, TypeScript, SASS, TailwindCSS, Shadcn UI, Vite, Grunt.js, Storybook, HMR, and more. | [Link](https://github.com/prappo/wordpress-plugin-boilerplate) | 2024-09-24 |
## Star History
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=birobirobiro/awesome-shadcn-ui&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=birobirobiro/awesome-shadcn-ui&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=birobirobiro/awesome-shadcn-ui&type=Date" />
</picture>
## Contributors
Thanks goes to all these wonderful people:
<a href="https://github.com/birobirobiro/awesome-shadcn-ui/graphs/contributors">
<img src="https://contrib.rocks/image?repo=birobirobiro/awesome-shadcn-ui" />
</a>
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Development & Contributing
### For the Website
- **Development Guide**: See [DEVELOPMENT.md](DEVELOPMENT.md) for setup, architecture, and configuration
- **Contributing**: Fork the repository, create a feature branch, and submit a PR
### For the Awesome List
- **Via Website**: Use the built-in submission form at [awesome-shadcn-ui.vercel.app](https://awesome-shadcn-ui.vercel.app/)
- **Via GitHub**: Follow the [PR template](.github/pull_request_template.md) when creating pull requests
- **Guidelines**: Ensure resources are shadcn/ui related, well-documented, and actively maintained
================================================
FILE: components.json
================================================
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "radix-lyra",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"menuColor": "default",
"menuAccent": "subtle",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {
"@diceui": "https://diceui.com/r/{name}.json"
}
}
================================================
FILE: next.config.mjs
================================================
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
eslint: {
ignoreDuringBuilds: true,
},
typescript: {
ignoreBuildErrors: true,
},
compiler: {
removeConsole:
process.env.NODE_ENV === "production" ? { exclude: ["error"] } : false,
},
};
export default nextConfig;
================================================
FILE: open-next.config.ts
================================================
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig();
================================================
FILE: package.json
================================================
{
"name": "awesome-shadcn-ui",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts",
"format": "prettier --write ./src",
"lint": "next lint",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@base-ui/react": "^1.2.0",
"@hugeicons/core-free-icons": "^3.3.0",
"@hugeicons/react": "^1.1.5",
"@next/third-parties": "^16.1.6",
"@octokit/rest": "^22.0.0",
"@opennextjs/cloudflare": "^1.16.1",
"@radix-ui/react-avatar": "1.1.9",
"@radix-ui/react-dialog": "1.1.13",
"@radix-ui/react-dropdown-menu": "2.1.14",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "1.1.13",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "2.2.4",
"@radix-ui/react-separator": "1.1.6",
"@radix-ui/react-slot": "1.2.2",
"@radix-ui/react-toggle": "1.1.8",
"@radix-ui/react-toggle-group": "1.1.9",
"@radix-ui/react-tooltip": "1.2.6",
"@radix-ui/react-use-controllable-state": "^1.2.2",
"@tailwindcss/postcss": "^4.1.11",
"axios": "^1.12.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"date-fns": "^4.1.0",
"embla-carousel-react": "^8.6.0",
"eslint-config-next": "^16.0.10",
"input-otp": "^1.4.2",
"lucide-react": "^0.509.0",
"motion": "^12.23.24",
"next": "16.0.10",
"next-themes": "^0.4.6",
"radix-ui": "latest",
"react": "^19.2.3",
"react-day-picker": "^9.14.0",
"react-dom": "^19.2.3",
"react-resizable-panels": "^4.6.5",
"recharts": "2.15.4",
"sonner": "^1.7.4",
"vaul": "^1.1.2"
},
"devDependencies": {
"@types/node": "24.0.10",
"@types/react": "^19",
"prettier": "^3",
"shadcn": "^3.8.5",
"tailwind-merge": "^2.6.0",
"tailwindcss": "^4.1.11",
"tw-animate-css": "^1.3.4",
"typescript": "5.8.3",
"wrangler": "^4.61.1"
}
}
================================================
FILE: postcss.config.mjs
================================================
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
================================================
FILE: scripts/add-dates.js
================================================
const fs = require('fs');
const path = 'README.md';
let content;
try {
content = fs.readFileSync(path, 'utf8');
} catch (error) {
console.error(`Error reading README.md: ${error.message}`);
process.exit(1);
}
const lines = content.split('\n');
const currentDate = new Date().toISOString().split('T')[0]; // e.g., 2025-07-02
const updatedLines = [];
let changesCount = 0;
console.log(`Processing README.md with ${lines.length} lines`);
// Function to validate ISO date format (YYYY-MM-DD)
function isValidDate(dateString) {
const regex = /^\d{4}-\d{2}-\d{2}$/;
if (!regex.test(dateString)) return false;
const date = new Date(dateString);
return date instanceof Date && !isNaN(date);
}
let inTable = false;
let tableHasDateColumn = false;
let headerLineCount = 0;
let dateColumnIndex = -1;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Reset table state when hitting a new section
if (line.startsWith('## ')) {
inTable = false;
tableHasDateColumn = false;
headerLineCount = 0;
dateColumnIndex = -1;
}
if (line.startsWith('|')) {
if (!inTable) {
// First line of a new table (header)
inTable = true;
headerLineCount = 1;
// Check if the table header has a date column and find its index
const parts = line.split('|').map(part => part.trim());
dateColumnIndex = parts.findIndex(part => part === 'Date');
tableHasDateColumn = dateColumnIndex > -1;
updatedLines.push(line);
continue;
} else {
headerLineCount++;
}
// Skip header and separator rows
if (headerLineCount <= 2) {
updatedLines.push(line);
continue;
}
// Process data rows
if (tableHasDateColumn && dateColumnIndex > 0) {
// Split the line into parts, preserving the original structure
const parts = line.split('|');
// Ensure we have enough parts for the date column
while (parts.length <= dateColumnIndex + 1) {
parts.push('');
}
// Get the current date value (trimmed)
const currentDateValue = parts[dateColumnIndex].trim();
// Only add date if it's empty, whitespace, or invalid
if (!currentDateValue || currentDateValue === '' || !isValidDate(currentDateValue)) {
parts[dateColumnIndex] = ` ${currentDate} `;
changesCount++;
}
// Reconstruct the line
const newLine = parts.join('|');
updatedLines.push(newLine);
} else {
// No date column or invalid structure, keep as is
updatedLines.push(line);
}
} else {
// Not a table row
updatedLines.push(line);
}
}
// Log summary
console.log(`Completed processing. Made ${changesCount} changes to date columns.`);
if (changesCount > 0) {
try {
fs.writeFileSync(path, updatedLines.join('\n'));
console.log('Changes written to README.md');
} catch (error) {
console.error(`Error writing to README.md: ${error.message}`);
process.exit(1);
}
} else {
console.log('No changes needed, file not modified');
}
================================================
FILE: scripts/format-readme.js
================================================
const fs = require('fs');
const path = 'README.md';
let content;
try {
content = fs.readFileSync(path, 'utf8');
} catch (err) {
console.error(`Error reading README.md: ${err.message}`);
process.exit(1);
}
// Split the content into lines
const lines = content.split('\n');
const updatedLines = [];
let inTable = false;
for (let line of lines) {
if (line.startsWith('|')) {
inTable = true;
// Remove internal line breaks and extra spaces in cells
const parts = line.split('|').map(cell => cell.replace(/\n/g, ' ').trim());
// Rebuild the line with pipes, keeping one line per entry
const formattedLine = '| ' + parts.filter((_, i) => i !== 0 && i !== parts.length - 1 || parts[i] !== '').join(' | ') + ' |';
updatedLines.push(formattedLine);
} else {
if (inTable) {
inTable = false;
}
updatedLines.push(line);
}
}
// Overwrite README.md with the updated lines
try {
fs.writeFileSync(path, updatedLines.join('\n'));
console.log('README.md formatted successfully!');
} catch (err) {
console.error(`Error writing README.md: ${err.message}`);
process.exit(1);
}
================================================
FILE: src/app/[...not-found]/page.tsx
================================================
import { notFound } from "next/navigation";
export default function CatchAllNotFound() {
notFound();
}
================================================
FILE: src/app/api/github/callback/route.ts
================================================
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const code = searchParams.get("code");
const state = searchParams.get("state");
const error = searchParams.get("error");
if (error) {
console.error("GitHub OAuth error:", error);
return NextResponse.redirect(
new URL(`/?error=${encodeURIComponent(error)}`, request.url),
);
}
if (!code) {
console.error("No authorization code received");
return NextResponse.redirect(new URL("/?error=no_code", request.url));
}
// For device flow, we don't need to handle the callback here
// The device flow handles everything through the device-flow endpoint
// This endpoint is just required by GitHub App configuration
console.log("GitHub App callback received:", { code, state });
return NextResponse.redirect(new URL("/?success=authorized", request.url));
}
================================================
FILE: src/app/api/github/device-flow/route.ts
================================================
import { ERROR_MESSAGES, GITHUB_CONFIG } from "@/lib/config";
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
try {
const body = await request.json();
if (body.action === "start") {
// Start the GitHub App device flow
const response = await fetch(GITHUB_CONFIG.DEVICE_FLOW_URL, {
method: "POST",
headers: GITHUB_CONFIG.API_HEADERS,
body: JSON.stringify({
client_id: GITHUB_CONFIG.CLIENT_ID,
scope: GITHUB_CONFIG.SCOPES.join(" "),
}),
});
if (!response.ok) {
const errorText = await response.text();
console.error(
"GitHub device flow start error:",
response.status,
errorText,
);
console.error("Request details:", {
client_id: GITHUB_CONFIG.CLIENT_ID,
scope: GITHUB_CONFIG.SCOPES.join(" "),
url: GITHUB_CONFIG.DEVICE_FLOW_URL,
});
throw new Error(
`${ERROR_MESSAGES.GITHUB_API}: ${response.status} - ${errorText}`,
);
}
const data = await response.json();
return NextResponse.json(data);
} else if (body.action === "poll") {
// Poll for the GitHub App user access token
const response = await fetch(GITHUB_CONFIG.ACCESS_TOKEN_URL, {
method: "POST",
headers: GITHUB_CONFIG.API_HEADERS,
body: JSON.stringify({
client_id: GITHUB_CONFIG.CLIENT_ID,
device_code: body.device_code,
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
}),
});
if (!response.ok) {
const errorText = await response.text();
console.error(
"GitHub device flow poll error:",
response.status,
errorText,
);
throw new Error(
`${ERROR_MESSAGES.GITHUB_API}: ${response.status} - ${errorText}`,
);
}
const data = await response.json();
return NextResponse.json(data);
}
return NextResponse.json(
{ error: ERROR_MESSAGES.INVALID_ACTION },
{ status: 400 },
);
} catch (error: any) {
console.error("GitHub device flow error:", error);
return NextResponse.json(
{ error: error.message || ERROR_MESSAGES.INTERNAL_SERVER },
{ status: 500 },
);
}
}
================================================
FILE: src/app/api/submit-resource/route.ts
================================================
import { NextRequest, NextResponse } from "next/server";
import { Octokit } from "@octokit/rest";
const GITHUB_CONFIG = {
REPO_OWNER: "birobirobiro",
REPO_NAME: "awesome-shadcn-ui",
BRANCH: "main",
};
interface SubmissionData {
name: string;
description: string;
url: string;
category: string;
}
function insertResourceIntoReadme(
readmeContent: string,
submission: SubmissionData,
): { content: string; error?: string } {
const lines = readmeContent.split("\n");
const newEntry = `| ${submission.name} | ${submission.description} | [Link](${submission.url}) |`;
let insertIndex = -1;
let inTargetSection = false;
let inTable = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (
line.startsWith("## ") &&
line.toLowerCase().includes(submission.category.toLowerCase())
) {
inTargetSection = true;
continue;
}
if (
inTargetSection &&
line.startsWith("## ") &&
!line.toLowerCase().includes(submission.category.toLowerCase())
) {
break;
}
if (inTargetSection && line.startsWith("| Name")) {
inTable = true;
continue;
}
if (inTable && line.match(/^\|[\s-]+\|/)) {
continue;
}
if (inTable && line.startsWith("|") && !line.match(/^\|[\s-]+\|/)) {
const parts = line.split("|").map((part) => part.trim());
if (parts.length >= 2) {
const existingName = parts[1];
const existingUrlMatch = line.match(/\[Link\]\(([^)]+)\)/);
const existingUrl = existingUrlMatch ? existingUrlMatch[1] : null;
if (existingName.toLowerCase() === submission.name.toLowerCase()) {
return {
content: "",
error: `Resource "${submission.name}" already exists in this section.`,
};
}
if (
existingUrl &&
existingUrl.toLowerCase() === submission.url.toLowerCase()
) {
return {
content: "",
error: `This URL already exists in this section.`,
};
}
if (existingName.toLowerCase() > submission.name.toLowerCase()) {
insertIndex = i;
break;
}
}
}
if (inTable && (!line.startsWith("|") || line.trim() === "")) {
if (insertIndex === -1) {
insertIndex = i;
}
break;
}
}
if (insertIndex === -1) {
return {
content: "",
error: `Could not find insertion point for category "${submission.category}".`,
};
}
lines.splice(insertIndex, 0, newEntry);
return { content: lines.join("\n") };
}
function generatePRBody(submission: SubmissionData): string {
return `---
name: "feat: Add new awesome resource"
about: "Propose adding a new awesome resource related to shadcn/ui"
labels:
- feature
---
## Describe the awesome resource you want to add
**What is it?**
${submission.description}
## **Which section does it belong to?**
- [x] ${submission.category}
## **Additional details (optional)**
Resource URL: ${submission.url}
## **Checklist**
- [x] Resource is automatically sorted alphabetically within its section.
- [x] Duplicate checking is performed automatically.
- [x] Table formatting is handled automatically.
- [x] Includes a valid and working link to the resource.
- [x] Automatically assigned the correct section to the resource.
`;
}
export async function POST(request: NextRequest) {
try {
const submission: SubmissionData = await request.json();
if (
!submission.name ||
!submission.description ||
!submission.url ||
!submission.category
) {
return NextResponse.json(
{ error: "All fields are required" },
{ status: 400 },
);
}
const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
return NextResponse.json(
{ error: "Server configuration error" },
{ status: 500 },
);
}
const octokit = new Octokit({ auth: githubToken });
const { data: readmeData } = await octokit.repos.getContent({
owner: GITHUB_CONFIG.REPO_OWNER,
repo: GITHUB_CONFIG.REPO_NAME,
path: "README.md",
});
if (
Array.isArray(readmeData) ||
!("content" in readmeData) ||
!readmeData.sha
) {
return NextResponse.json(
{ error: "Could not fetch README" },
{ status: 500 },
);
}
const currentContent = Buffer.from(readmeData.content, "base64").toString();
const insertResult = insertResourceIntoReadme(currentContent, submission);
if (insertResult.error) {
return NextResponse.json({ error: insertResult.error }, { status: 409 });
}
const updatedContent = insertResult.content;
const branchName = `add-${submission.name.toLowerCase().replace(/[^a-z0-9]/g, "-")}-${Date.now()}`;
const { data: ref } = await octokit.git.getRef({
owner: GITHUB_CONFIG.REPO_OWNER,
repo: GITHUB_CONFIG.REPO_NAME,
ref: "heads/main",
});
await octokit.git.createRef({
owner: GITHUB_CONFIG.REPO_OWNER,
repo: GITHUB_CONFIG.REPO_NAME,
ref: `refs/heads/${branchName}`,
sha: ref.object.sha,
});
await octokit.repos.createOrUpdateFileContents({
owner: GITHUB_CONFIG.REPO_OWNER,
repo: GITHUB_CONFIG.REPO_NAME,
path: "README.md",
message: `feat: Add ${submission.name}`,
content: Buffer.from(updatedContent).toString("base64"),
sha: readmeData.sha,
branch: branchName,
});
const prBody = generatePRBody(submission);
const { data: pr } = await octokit.pulls.create({
owner: GITHUB_CONFIG.REPO_OWNER,
repo: GITHUB_CONFIG.REPO_NAME,
title: `feat: Add ${submission.name}`,
head: branchName,
base: "main",
body: prBody,
});
return NextResponse.json({
success: true,
prNumber: pr.number,
prUrl: pr.html_url,
});
} catch (error: any) {
console.error("Error creating PR:", error);
return NextResponse.json(
{ error: error.message || "Failed to create pull request" },
{ status: 500 },
);
}
}
================================================
FILE: src/app/bookmarks/error.tsx
================================================
"use client";
import { Button } from "@/components/ui/button";
import { AlertTriangle, ArrowLeft, Home, RefreshCw } from "lucide-react";
import Link from "next/link";
import { useEffect } from "react";
export default function BookmarksError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Bookmarks error:", error);
}, [error]);
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center space-y-6">
<div className="flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-destructive/10 dark:bg-destructive/20">
<AlertTriangle className="h-8 w-8 sm:h-10 sm:w-10 text-destructive" />
</div>
<div className="space-y-2">
<h1 className="text-2xl sm:text-3xl font-bold text-foreground">
Failed to load bookmarks
</h1>
<p className="text-sm sm:text-base text-muted-foreground max-w-md">
We couldn't load this bookmarks page. The bookmarks might not exist
or there's a temporary issue.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
<Button onClick={reset} className="flex items-center gap-2">
<RefreshCw className="h-4 w-4" />
Try again
</Button>
<Button variant="outline" asChild>
<Link href="/categories" className="flex items-center gap-2">
<ArrowLeft className="h-4 w-4" />
Back to categories
</Link>
</Button>
<Button variant="outline" asChild>
<Link href="/" className="flex items-center gap-2">
<Home className="h-4 w-4" />
Homepage
</Link>
</Button>
</div>
{process.env.NODE_ENV === "development" && (
<details className="mt-6 p-4 bg-muted text-left max-w-2xl w-full">
<summary className="cursor-pointer text-sm font-medium">
Error Details (Development)
</summary>
<pre className="mt-2 text-xs text-muted-foreground overflow-auto">
{error.message}
{error.stack && `\n\n${error.stack}`}
</pre>
</details>
)}
</div>
</div>
);
}
================================================
FILE: src/app/bookmarks/page.tsx
================================================
"use client";
import { ItemGrid } from "@/components/item-grid";
import { PageHeader } from "@/components/layout/page-header";
import { PaginationControls } from "@/components/pagination-controls";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { useBookmarks } from "@/hooks/use-bookmark";
import { useDebounce } from "@/hooks/use-debounce";
import { fetchAndParseReadme, Resource } from "@/hooks/use-readme";
import { Bookmark, Search } from "lucide-react";
import { motion } from "motion/react";
import Link from "next/link";
import { useCallback, useEffect, useState } from "react";
const ITEMS_PER_PAGE_OPTIONS = [20, 40, 60, 80];
export default function BookmarksPage() {
const [items, setItems] = useState<Resource[]>([]);
const [searchQuery, setSearchQuery] = useState("");
const [filteredItems, setFilteredItems] = useState<Resource[]>([]);
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(ITEMS_PER_PAGE_OPTIONS[0]);
const [isLoading, setIsLoading] = useState(true);
const [showClearDialog, setShowClearDialog] = useState(false);
const {
bookmarkedItems,
toggleBookmark,
clearBookmarks,
isLoading: isBookmarkLoading,
} = useBookmarks();
const debouncedSearchQuery = useDebounce(searchQuery, 300);
useEffect(() => {
async function fetchData() {
try {
setIsLoading(true);
const fetchedResources = await fetchAndParseReadme();
const bookmarkedResources = fetchedResources.filter((item) =>
bookmarkedItems.includes(item.id),
);
setItems(bookmarkedResources);
setFilteredItems(bookmarkedResources);
} catch (error) {
console.error("Error fetching bookmarked items:", error);
} finally {
setIsLoading(false);
}
}
if (!isBookmarkLoading) {
fetchData();
}
}, [bookmarkedItems, isBookmarkLoading]);
useEffect(() => {
if (debouncedSearchQuery) {
const filtered = items.filter(
(item) =>
item.name
?.toLowerCase()
.includes(debouncedSearchQuery.toLowerCase()) ||
item.description
?.toLowerCase()
.includes(debouncedSearchQuery.toLowerCase()),
);
setFilteredItems(filtered);
} else {
setFilteredItems(items);
}
setCurrentPage(1);
}, [debouncedSearchQuery, items]);
const totalPages = Math.ceil(filteredItems.length / itemsPerPage);
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = filteredItems.slice(indexOfFirstItem, indexOfLastItem);
const handlePageChange = useCallback((pageNumber: number) => {
setCurrentPage(pageNumber);
}, []);
const handleItemsPerPageChange = useCallback((value: string) => {
setItemsPerPage(Number(value));
setCurrentPage(1);
}, []);
const handleClearBookmarks = useCallback(() => {
clearBookmarks();
setShowClearDialog(false);
setSearchQuery("");
}, [clearBookmarks]);
if (isLoading || isBookmarkLoading) {
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="space-y-6 sm:space-y-8">
<div className="space-y-3 sm:space-y-4">
<Skeleton className="h-8 sm:h-12 w-full max-w-96" />
<Skeleton className="h-4 sm:h-6 w-full max-w-64" />
<div className="flex items-center gap-2 sm:gap-4">
<Skeleton className="h-4 w-20" />
<Skeleton className="h-4 w-2" />
<Skeleton className="h-4 w-16" />
</div>
<Skeleton className="h-8 sm:h-10 w-full max-w-md" />
</div>
<div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
{[...Array(6)].map((_, i) => (
<div key={i} className="border p-4 sm:p-6 space-y-3 sm:space-y-4">
<div className="space-y-2">
<Skeleton className="h-5 sm:h-6 w-3/4" />
<Skeleton className="h-4 sm:h-5 w-20" />
</div>
<div className="space-y-2">
<Skeleton className="h-3 sm:h-4 w-full" />
<Skeleton className="h-3 sm:h-4 w-5/6" />
</div>
<div className="flex justify-between items-center">
<Skeleton className="h-8 w-8 sm:h-9 sm:w-9" />
<Skeleton className="h-8 w-8 sm:h-9 sm:w-9" />
</div>
</div>
))}
</div>
<div className="flex flex-col sm:flex-row items-center justify-between space-y-3 sm:space-y-0 gap-3 sm:gap-0">
<div className="flex items-center space-x-2">
<Skeleton className="h-4 w-20 sm:w-24" />
<Skeleton className="h-8 sm:h-10 w-[60px] sm:w-[70px]" />
</div>
<div className="flex items-center space-x-1 sm:space-x-2">
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
<Skeleton className="h-4 w-12 sm:w-16" />
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
</div>
</div>
</div>
</div>
);
}
return (
<motion.div
className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<PageHeader
title="Bookmarks"
description="Your saved shadcn/ui resources"
breadcrumbs={[{ label: "Bookmarks", href: "/bookmarks" }]}
actions={
<div className="space-y-3 sm:space-y-4 w-full">
<div className="flex items-center gap-2 sm:gap-4 text-xs sm:text-sm text-muted-foreground">
<span>
{items.length} {items.length === 1 ? "bookmark" : "bookmarks"}
</span>
</div>
<div className="flex flex-col sm:flex-row gap-2 sm:gap-3 w-full">
<div className="w-full sm:flex-1 max-w-md">
<Input
type="text"
placeholder="Search your bookmarks..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="h-10"
/>
</div>
{items.length > 0 && (
<div className="p-2">
<Button
variant="outline"
onClick={() => setShowClearDialog(true)}
className="w-full sm:w-auto text-sm sm:text-base"
>
Clear All
</Button>
</div>
)}
</div>
</div>
}
/>
<div className="min-h-[400px] mb-6 sm:mb-8">
{items.length === 0 ? (
<div className="flex flex-col items-center justify-center h-[400px] text-center px-4">
<div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center mb-4">
<Bookmark className="h-8 w-8 text-muted-foreground" />
</div>
<h3 className="text-lg font-semibold mb-2">No bookmarks yet</h3>
<p className="text-sm text-muted-foreground mb-6 max-w-md">
Start saving your favorite shadcn/ui resources by clicking the
bookmark icon on any item.
</p>
<Button asChild>
<Link href="/">Browse resources</Link>
</Button>
</div>
) : filteredItems.length === 0 && debouncedSearchQuery ? (
<div className="flex flex-col items-center justify-center h-[400px] text-center px-4">
<div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center mb-4">
<Search className="h-8 w-8 text-muted-foreground" />
</div>
<h3 className="text-lg font-semibold mb-2">No results found</h3>
<p className="text-sm text-muted-foreground mb-6">
No bookmarks match your search for "{debouncedSearchQuery}"
</p>
<Button variant="outline" onClick={() => setSearchQuery("")}>
Clear search
</Button>
</div>
) : (
<ItemGrid
items={currentItems}
bookmarkedItems={bookmarkedItems}
onBookmark={toggleBookmark}
isBookmarkLoading={isBookmarkLoading}
/>
)}
</div>
{filteredItems.length > 0 && items.length > 0 && (
<div>
<PaginationControls
currentPage={currentPage}
totalPages={totalPages}
itemsPerPage={itemsPerPage}
handlePageChange={handlePageChange}
handleItemsPerPageChange={handleItemsPerPageChange}
itemsPerPageOptions={ITEMS_PER_PAGE_OPTIONS}
/>
</div>
)}
{items.length > 0 && (
<div className="text-xs sm:text-sm text-muted-foreground text-center mt-4 sm:mt-6">
Showing {indexOfFirstItem + 1} -{" "}
{Math.min(indexOfLastItem, filteredItems.length)} of{" "}
{filteredItems.length}{" "}
{filteredItems.length === 1 ? "bookmark" : "bookmarks"}
</div>
)}
<Dialog open={showClearDialog} onOpenChange={setShowClearDialog}>
<DialogContent>
<DialogHeader>
<DialogTitle>Clear All Bookmarks?</DialogTitle>
<DialogDescription>
This will remove all your bookmarked items. This action cannot be
undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={() => setShowClearDialog(false)}>
Cancel
</Button>
<Button variant="destructive" onClick={handleClearBookmarks}>
Clear All
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</motion.div>
);
}
================================================
FILE: src/app/categories/[category]/[id]/error.tsx
================================================
"use client";
import { Button } from "@/components/ui/button";
import { AlertTriangle, ArrowLeft, Home, RefreshCw } from "lucide-react";
import Link from "next/link";
import { useEffect } from "react";
export default function ItemError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Item error:", error);
}, [error]);
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center space-y-6">
<div className="flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-destructive/10 dark:bg-destructive/20">
<AlertTriangle className="h-8 w-8 sm:h-10 sm:w-10 text-destructive" />
</div>
<div className="space-y-2">
<h1 className="text-2xl sm:text-3xl font-bold text-foreground">
Failed to load item
</h1>
<p className="text-sm sm:text-base text-muted-foreground max-w-md">
We couldn't load this item page. The item might not exist or there's
a temporary issue.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
<Button onClick={reset} className="flex items-center gap-2">
<RefreshCw className="h-4 w-4" />
Try again
</Button>
<Button variant="outline" asChild>
<Link href="/categories" className="flex items-center gap-2">
<ArrowLeft className="h-4 w-4" />
Back to categories
</Link>
</Button>
<Button variant="outline" asChild>
<Link href="/" className="flex items-center gap-2">
<Home className="h-4 w-4" />
Homepage
</Link>
</Button>
</div>
{process.env.NODE_ENV === "development" && (
<details className="mt-6 p-4 bg-muted text-left max-w-2xl w-full">
<summary className="cursor-pointer text-sm font-medium">
Error Details (Development)
</summary>
<pre className="mt-2 text-xs text-muted-foreground overflow-auto">
{error.message}
{error.stack && `\n\n${error.stack}`}
</pre>
</details>
)}
</div>
</div>
);
}
================================================
FILE: src/app/categories/[category]/[id]/page.tsx
================================================
"use client";
import { PageHeader } from "@/components/layout/page-header";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { fetchAndParseReadme, Resource } from "@/hooks/use-readme";
import { useWebsitePreview } from "@/hooks/use-website-preview";
import { categoryNameToSlug, slugToCategoryName } from "@/lib/slugs";
import { format, isValid, parseISO } from "date-fns";
import {
Calendar,
ExternalLink,
Github,
Globe,
Tag,
Image,
} from "lucide-react";
import { motion } from "motion/react";
import Link from "next/link";
import { use, useEffect, useState } from "react";
interface ItemPageProps {
params: Promise<{
category: string;
id: string;
}>;
}
const CATEGORY_DESCRIPTIONS: Record<string, string> = {
"Libs and Components":
"Essential libraries and reusable components built with shadcn/ui",
Registries: "Component registries and collections for shadcn/ui",
"Plugins and Extensions":
"Tools and extensions that enhance your shadcn/ui workflow",
"Colors and Customizations":
"Themes, color palettes, and customization utilities",
Animations: "Animation libraries and motion components for shadcn/ui",
Tools: "Development tools, generators, and utilities for shadcn/ui projects",
"Websites and Portfolios Inspirations":
"Real-world examples and inspiration for your projects",
Platforms: "Platforms and services that integrate with shadcn/ui",
Ports: "Ports of shadcn/ui to other frameworks and technologies",
"Design System": "Complete design systems and component libraries",
"Boilerplates / Templates":
"Starter templates and boilerplates for quick project setup",
};
export default function ItemPage({ params }: ItemPageProps) {
const resolvedParams = use(params);
const [item, setItem] = useState<Resource | null>(null);
const [relatedItems, setRelatedItems] = useState<Resource[]>([]);
const [faviconUrl, setFaviconUrl] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const categorySlug = resolvedParams.category;
const categoryName = slugToCategoryName(categorySlug);
const itemId = resolvedParams.id;
useEffect(() => {
async function fetchData() {
try {
setIsLoading(true);
const fetchedResources = await fetchAndParseReadme();
const foundItem = fetchedResources.find(
(resource) => resource.id === itemId,
);
if (foundItem) {
setItem(foundItem);
const related = fetchedResources
.filter(
(resource) =>
resource.category === foundItem.category &&
resource.id !== foundItem.id,
)
.slice(0, 6);
setRelatedItems(related);
const domain = new URL(foundItem.url).hostname;
setFaviconUrl(`https://icons.duckduckgo.com/ip3/${domain}.ico`);
}
} catch (error) {
console.error("Error fetching item data:", error);
} finally {
setIsLoading(false);
}
}
fetchData();
}, [itemId, categoryName]);
const formatDate = (dateString: string) => {
if (dateString === "Unknown") return "Unknown";
const date = parseISO(dateString);
return isValid(date) ? format(date, "MMMM d, yyyy") : "Unknown";
};
const isGitHubUrl = (url: string) => {
return url.includes("github.com");
};
if (isLoading) {
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="space-y-6 sm:space-y-8">
<div className="space-y-3 sm:space-y-4">
<Skeleton className="h-8 sm:h-12 w-full max-w-96" />
<Skeleton className="h-4 sm:h-6 w-full max-w-64" />
<div className="flex items-center gap-2 sm:gap-4 text-xs sm:text-sm">
<div className="flex items-center gap-2">
<Skeleton className="h-3 w-3 sm:h-4 sm:w-4" />
<Skeleton className="h-3 w-16 sm:h-4 sm:w-20" />
</div>
<Skeleton className="h-3 w-2 sm:h-4" />
<div className="flex items-center gap-2">
<Skeleton className="h-3 w-3 sm:h-4 sm:w-4" />
<Skeleton className="h-3 w-12 sm:h-4 sm:w-16" />
</div>
</div>
</div>
<div className="grid gap-6 sm:gap-8 grid-cols-1 lg:grid-cols-3">
<div className="lg:col-span-2 space-y-4 sm:space-y-6">
<Card>
<CardHeader className="p-4">
<div className="flex items-center gap-2">
<Skeleton className="h-4 w-4 sm:h-5 sm:w-5" />
<Skeleton className="h-5 w-16 sm:h-6 sm:w-16" />
</div>
</CardHeader>
<CardContent className="p-4 pt-0">
<div className="h-96 sm:h-[44rem] w-full bg-muted/50 flex items-center justify-center">
<div className="text-center">
<Globe className="h-8 w-8 mx-auto mb-2 text-muted-foreground/50" />
<Skeleton className="h-4 w-24 mx-auto" />
</div>
</div>
</CardContent>
</Card>
</div>
<div className="space-y-4 sm:space-y-6">
<Card>
<CardHeader className="p-4">
<Skeleton className="h-5 w-16" />
</CardHeader>
<CardContent className="p-4 pt-0 space-y-3">
<Skeleton className="h-5 w-20" />
<div className="space-y-2">
<Skeleton className="h-3 w-full sm:h-4" />
<Skeleton className="h-3 w-3/4 sm:h-4" />
</div>
<div className="space-y-2">
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
</div>
</CardContent>
</Card>
<Card>
<CardHeader className="p-4">
<Skeleton className="h-5 w-24 sm:h-6" />
</CardHeader>
<CardContent className="p-4 pt-0 space-y-3">
{[...Array(6)].map((_, i) => (
<div key={i} className="p-3 border space-y-2">
<Skeleton className="h-3 w-3/4 sm:h-4" />
<Skeleton className="h-3 w-full" />
</div>
))}
</CardContent>
</Card>
</div>
</div>
</div>
</div>
);
}
if (!item) {
return null;
}
// Component to render preview based on state
const WebsitePreview = () => {
const { previewState, screenshotUrl } = useWebsitePreview({
url: item.url,
name: item.name,
});
if (previewState === "loading") {
return (
<div className="w-full h-full flex items-center justify-center bg-muted">
<div className="flex flex-col items-center gap-3">
<div className="w-8 h-8 border-4 border-primary/30 border-t-primary rounded-full animate-spin" />
<p className="text-sm text-muted-foreground">Loading preview...</p>
</div>
</div>
);
}
if (previewState === "iframe") {
return (
<>
<iframe
src={item.url}
className="w-full h-full border-0"
title={`Preview of ${item.name}`}
sandbox="allow-scripts allow-same-origin"
/>
<a
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="absolute inset-0 bg-transparent cursor-pointer z-10"
title={`Visit ${item.name} - Opens in new tab`}
aria-label={`Visit ${item.name} website`}
>
<span className="sr-only">Visit {item.name} website</span>
</a>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent transition-all duration-300 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 pointer-events-none">
<ExternalLink className="h-8 w-8 text-white mb-2" />
<p className="text-lg font-medium text-white">Visit website</p>
<p className="text-sm text-white/80 mt-1">Click to open</p>
</div>
</>
);
}
if (previewState === "screenshot" && screenshotUrl) {
return (
<>
<img
src={screenshotUrl}
alt={`${item.name} preview`}
className="w-full h-full object-cover object-top"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent transition-all duration-300 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 pointer-events-none">
<ExternalLink className="h-8 w-8 text-white mb-2" />
<p className="text-lg font-medium text-white">Visit website</p>
<p className="text-sm text-white/80 mt-1">Screenshot preview</p>
</div>
<a
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="absolute inset-0 bg-transparent cursor-pointer z-10"
title={`Visit ${item.name} - Opens in new tab`}
aria-label={`Visit ${item.name} website`}
>
<span className="sr-only">Visit {item.name} website</span>
</a>
</>
);
}
// Fallback state
return (
<div className="absolute inset-0 bg-muted flex flex-col items-center justify-center text-center p-6">
<Image className="h-12 w-12 text-muted-foreground mb-3" />
<p className="text-sm text-muted-foreground mb-2">
Preview not available
</p>
<p className="text-xs text-muted-foreground/70 mb-4 max-w-xs">
This website blocks embedding. Click below to visit directly.
</p>
<Button asChild size="sm">
<a
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2"
>
<ExternalLink className="h-4 w-4" />
Visit Website
</a>
</Button>
</div>
);
};
return (
<motion.div
className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<PageHeader
title={item.name}
description={item.description}
icon={
faviconUrl ? (
<img
src={faviconUrl}
alt={`${item.name} favicon`}
className="h-6 w-6 sm:h-8 sm:w-8 flex-shrink-0"
onError={() => setFaviconUrl(null)}
/>
) : (
<div className="h-6 w-6 sm:h-8 sm:w-8 bg-muted flex items-center justify-center flex-shrink-0">
<Globe className="h-3 w-3 sm:h-4 sm:w-4 text-muted-foreground" />
</div>
)
}
breadcrumbs={[
{ label: "Categories", href: "/categories" },
{ label: categoryName, href: `/categories/${categorySlug}` },
{
label: item.name || "Loading...",
href: `/categories/${categorySlug}/${itemId}`,
},
]}
actions={
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-2 sm:gap-4 text-xs sm:text-sm text-muted-foreground">
<div className="flex items-center gap-2">
<Calendar className="h-3 w-3 sm:h-4 sm:w-4 flex-shrink-0" />
<span className="break-words">Added {formatDate(item.date)}</span>
</div>
<span className="hidden sm:inline">•</span>
<div className="flex items-center gap-2">
<Tag className="h-3 w-3 sm:h-4 sm:w-4 flex-shrink-0" />
<span className="break-words">{item.category}</span>
</div>
</div>
}
/>
<div className="min-h-screen grid gap-6 sm:gap-8 grid-cols-1 lg:grid-cols-3">
<motion.div
className="lg:col-span-2 space-y-6 sm:space-y-8"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
<Card>
<CardHeader className="p-4 pb-3">
<CardTitle className="flex items-center gap-2 text-base sm:text-lg">
<Globe className="h-4 w-4 sm:h-5 sm:w-5" />
Preview
</CardTitle>
</CardHeader>
<CardContent className="p-4 pt-0">
<div className="relative h-[300px] sm:h-[500px] lg:h-[600px] overflow-hidden border group">
<WebsitePreview />
</div>
</CardContent>
</Card>
</motion.div>
<motion.div
className="space-y-4 sm:space-y-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.4 }}
>
<Card>
<CardHeader className="p-4">
<CardTitle className="text-base sm:text-lg">Category</CardTitle>
</CardHeader>
<CardContent className="p-4 pt-0 space-y-3">
<Badge variant="secondary" className="text-xs sm:text-sm">
{item.category}
</Badge>
<p className="text-xs sm:text-sm text-muted-foreground break-words">
{CATEGORY_DESCRIPTIONS[item.category] ||
"A collection of shadcn/ui related resources"}
</p>
<div className="space-y-2">
<Button
variant="outline"
size="sm"
asChild
className="w-full text-xs sm:text-sm"
>
<Link
href={`/categories/${categoryNameToSlug(item.category)}`}
>
View all in {item.category}
</Link>
</Button>
<Button asChild className="w-full text-xs sm:text-sm">
<a
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2"
>
{isGitHubUrl(item.url) ? (
<Github className="h-3 w-3 sm:h-4 sm:w-4" />
) : (
<ExternalLink className="h-3 w-3 sm:h-4 sm:w-4" />
)}
{isGitHubUrl(item.url) ? "View on GitHub" : "Visit Website"}
</a>
</Button>
</div>
</CardContent>
</Card>
{relatedItems.length > 0 && (
<Card>
<CardHeader className="p-4">
<CardTitle className="text-base sm:text-lg">
Related Items
</CardTitle>
</CardHeader>
<CardContent className="p-4 pt-0 space-y-3">
{relatedItems.map((relatedItem) => (
<Link
key={relatedItem.id}
href={`/categories/${categoryNameToSlug(item.category)}/${relatedItem.id}`}
className="block group"
>
<div className="relative overflow-hidden flex items-center justify-between p-3 border hover:bg-muted/50 hover:border-primary/20 transition-all duration-200 cursor-pointer group-hover:shadow-sm">
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-all duration-300 pointer-events-none">
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-transparent dark:from-primary/10" />
</div>
<div className="relative z-10 flex items-center justify-between w-full">
<div className="flex-1 min-w-0">
<p className="text-xs sm:text-sm font-medium truncate group-hover:text-primary transition-colors">
{relatedItem.name}
</p>
<p className="text-xs text-muted-foreground truncate group-hover:text-muted-foreground/80 transition-colors">
{relatedItem.description}
</p>
</div>
<div className="flex items-center gap-1 sm:gap-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex-shrink-0">
<span className="text-xs text-muted-foreground hidden sm:inline">
View
</span>
<ExternalLink className="h-3 w-3 text-muted-foreground group-hover:text-primary transition-colors" />
</div>
</div>
</div>
</Link>
))}
</CardContent>
</Card>
)}
</motion.div>
</div>
</motion.div>
);
}
================================================
FILE: src/app/categories/[category]/error.tsx
================================================
"use client";
import { Button } from "@/components/ui/button";
import { AlertTriangle, ArrowLeft, Home, RefreshCw } from "lucide-react";
import Link from "next/link";
import { useEffect } from "react";
export default function CategoryError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Category error:", error);
}, [error]);
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center space-y-6">
<div className="flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-destructive/10 dark:bg-destructive/20">
<AlertTriangle className="h-8 w-8 sm:h-10 sm:w-10 text-destructive" />
</div>
<div className="space-y-2">
<h1 className="text-2xl sm:text-3xl font-bold text-foreground">
Failed to load category
</h1>
<p className="text-sm sm:text-base text-muted-foreground max-w-md">
We couldn't load this category page. The category might not exist or
there's a temporary issue.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
<Button onClick={reset} className="flex items-center gap-2">
<RefreshCw className="h-4 w-4" />
Try again
</Button>
<Button variant="outline" asChild>
<Link href="/categories" className="flex items-center gap-2">
<ArrowLeft className="h-4 w-4" />
Back to categories
</Link>
</Button>
<Button variant="outline" asChild>
<Link href="/" className="flex items-center gap-2">
<Home className="h-4 w-4" />
Homepage
</Link>
</Button>
</div>
{process.env.NODE_ENV === "development" && (
<details className="mt-6 p-4 bg-muted text-left max-w-2xl w-full">
<summary className="cursor-pointer text-sm font-medium">
Error Details (Development)
</summary>
<pre className="mt-2 text-xs text-muted-foreground overflow-auto">
{error.message}
{error.stack && `\n\n${error.stack}`}
</pre>
</details>
)}
</div>
</div>
);
}
================================================
FILE: src/app/categories/[category]/page.tsx
================================================
"use client";
import { ItemGrid } from "@/components/item-grid";
import { PageHeader } from "@/components/layout/page-header";
import { PaginationControls } from "@/components/pagination-controls";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { useBookmarks } from "@/hooks/use-bookmark";
import { useDebounce } from "@/hooks/use-debounce";
import { fetchAndParseReadme, Resource } from "@/hooks/use-readme";
import { slugToCategoryName } from "@/lib/slugs";
import { motion } from "motion/react";
import { use, useCallback, useEffect, useState } from "react";
interface CategoryPageProps {
params: Promise<{
category: string;
}>;
}
const CATEGORY_DESCRIPTIONS: Record<string, string> = {
"Libs and Components":
"Essential libraries and reusable components built with shadcn/ui",
Registries: "Component registries and collections for shadcn/ui",
"Plugins and Extensions":
"Tools and extensions that enhance your shadcn/ui workflow",
"Colors and Customizations":
"Themes, color palettes, and customization utilities",
Animations: "Animation libraries and motion components for shadcn/ui",
Tools: "Development tools, generators, and utilities for shadcn/ui projects",
"Websites and Portfolios Inspirations":
"Real-world examples and inspiration for your projects",
Platforms: "Platforms and services that integrate with shadcn/ui",
Ports: "Ports of shadcn/ui to other frameworks and technologies",
"Design System": "Complete design systems and component libraries",
"Boilerplates / Templates":
"Starter templates and boilerplates for quick project setup",
};
const ITEMS_PER_PAGE_OPTIONS = [20, 40, 60, 80];
export default function CategoryPage({ params }: CategoryPageProps) {
const resolvedParams = use(params);
const [items, setItems] = useState<Resource[]>([]);
const [searchQuery, setSearchQuery] = useState("");
const [filteredItems, setFilteredItems] = useState<Resource[]>([]);
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(ITEMS_PER_PAGE_OPTIONS[1]);
const [isLoading, setIsLoading] = useState(true);
const {
bookmarkedItems,
toggleBookmark,
isLoading: isBookmarkLoading,
} = useBookmarks();
const debouncedSearchQuery = useDebounce(searchQuery, 300);
const categorySlug = resolvedParams.category;
const categoryName = slugToCategoryName(categorySlug);
useEffect(() => {
async function fetchData() {
try {
setIsLoading(true);
const fetchedResources = await fetchAndParseReadme();
const categoryItems = fetchedResources.filter(
(item) => item.category === categoryName,
);
setItems(categoryItems);
setFilteredItems(categoryItems);
} catch (error) {
console.error("Error fetching category items:", error);
} finally {
setIsLoading(false);
}
}
fetchData();
}, [categoryName]);
useEffect(() => {
if (debouncedSearchQuery) {
const filtered = items.filter(
(item) =>
item.name
?.toLowerCase()
.includes(debouncedSearchQuery.toLowerCase()) ||
item.description
?.toLowerCase()
.includes(debouncedSearchQuery.toLowerCase()),
);
setFilteredItems(filtered);
} else {
setFilteredItems(items);
}
setCurrentPage(1);
}, [debouncedSearchQuery, items]);
const totalPages = Math.ceil(filteredItems.length / itemsPerPage);
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = filteredItems.slice(indexOfFirstItem, indexOfLastItem);
const handlePageChange = useCallback((pageNumber: number) => {
setCurrentPage(pageNumber);
}, []);
const handleItemsPerPageChange = useCallback((value: string) => {
setItemsPerPage(Number(value));
setCurrentPage(1);
}, []);
if (isLoading) {
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="space-y-6 sm:space-y-8">
<div className="space-y-3 sm:space-y-4">
<Skeleton className="h-8 sm:h-12 w-full max-w-96" />
<Skeleton className="h-4 sm:h-6 w-full max-w-64" />
<div className="flex items-center gap-2 sm:gap-4">
<Skeleton className="h-4 w-20" />
<Skeleton className="h-4 w-2" />
<Skeleton className="h-4 w-16" />
</div>
<Skeleton className="h-8 sm:h-10 w-full max-w-md" />
</div>
<div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
{[...Array(6)].map((_, i) => (
<div key={i} className="border p-4 sm:p-6 space-y-3 sm:space-y-4">
<div className="space-y-2">
<Skeleton className="h-5 sm:h-6 w-3/4" />
<Skeleton className="h-4 sm:h-5 w-20" />
</div>
<div className="space-y-2">
<Skeleton className="h-3 sm:h-4 w-full" />
<Skeleton className="h-3 sm:h-4 w-5/6" />
</div>
<div className="flex justify-between items-center">
<Skeleton className="h-8 w-8 sm:h-9 sm:w-9" />
<Skeleton className="h-8 w-8 sm:h-9 sm:w-9" />
</div>
</div>
))}
</div>
<div className="flex flex-col sm:flex-row items-center justify-between space-y-3 sm:space-y-0 gap-3 sm:gap-0">
<div className="flex items-center space-x-2">
<Skeleton className="h-4 w-20 sm:w-24" />
<Skeleton className="h-8 sm:h-10 w-[60px] sm:w-[70px]" />
</div>
<div className="flex items-center space-x-1 sm:space-x-2">
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
<Skeleton className="h-4 w-12 sm:w-16" />
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
<Skeleton className="h-8 w-8 sm:h-10 sm:w-10" />
</div>
</div>
</div>
</div>
);
}
return (
<motion.div
className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<PageHeader
title={categoryName}
description={
CATEGORY_DESCRIPTIONS[categoryName] ||
"A collection of shadcn/ui related resources"
}
breadcrumbs={[
{ label: "Categories", href: "/categories" },
{ label: categoryName, href: `/categories/${categorySlug}` },
]}
actions={
<div className="space-y-3 sm:space-y-4 w-full">
<div className="flex items-center gap-2 sm:gap-4 text-xs sm:text-sm text-muted-foreground">
<span>
{items.length} {items.length === 1 ? "item" : "items"}
</span>
<span className="hidden sm:inline">•</span>
<span className="hidden sm:inline">Category</span>
</div>
<div className="w-full max-w-md">
<Input
type="text"
placeholder="Search within this category..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="h-10"
/>
</div>
</div>
}
/>
<div className="min-h-screen mb-6 sm:mb-8">
<ItemGrid
items={currentItems}
bookmarkedItems={bookmarkedItems}
onBookmark={toggleBookmark}
isBookmarkLoading={isBookmarkLoading}
/>
</div>
{filteredItems.length > 0 && (
<div>
<PaginationControls
currentPage={currentPage}
totalPages={totalPages}
itemsPerPage={itemsPerPage}
handlePageChange={handlePageChange}
handleItemsPerPageChange={handleItemsPerPageChange}
itemsPerPageOptions={ITEMS_PER_PAGE_OPTIONS}
/>
</div>
)}
<div className="text-xs sm:text-sm text-muted-foreground text-center mt-4 sm:mt-6">
Showing {indexOfFirstItem + 1} -{" "}
{Math.min(indexOfLastItem, filteredItems.length)} of{" "}
{filteredItems.length} items
</div>
{filteredItems.length === 0 && debouncedSearchQuery && (
<div className="text-center py-8 sm:py-12 px-4">
<p className="text-muted-foreground mb-4 text-sm sm:text-base break-words">
No items found for "{debouncedSearchQuery}"
</p>
<Button
variant="outline"
onClick={() => setSearchQuery("")}
className="text-sm sm:text-base"
>
Clear search
</Button>
</div>
)}
</motion.div>
);
}
================================================
FILE: src/app/categories/error.tsx
================================================
"use client";
import { Button } from "@/components/ui/button";
import { AlertTriangle, Home, RefreshCw } from "lucide-react";
import Link from "next/link";
import { useEffect } from "react";
export default function CategoriesError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Categories error:", error);
}, [error]);
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center space-y-6">
<div className="flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-destructive/10 dark:bg-destructive/20">
<AlertTriangle className="h-8 w-8 sm:h-10 sm:w-10 text-destructive" />
</div>
<div className="space-y-2">
<h1 className="text-2xl sm:text-3xl font-bold text-foreground">
Failed to load categories
</h1>
<p className="text-sm sm:text-base text-muted-foreground max-w-md">
We couldn't load the categories page. This might be a temporary
issue with our data source.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
<Button onClick={reset} className="flex items-center gap-2">
<RefreshCw className="h-4 w-4" />
Try again
</Button>
<Button variant="outline" asChild>
<Link href="/" className="flex items-center gap-2">
<Home className="h-4 w-4" />
Go to homepage
</Link>
</Button>
</div>
{process.env.NODE_ENV === "development" && (
<details className="mt-6 p-4 bg-muted text-left max-w-2xl w-full">
<summary className="cursor-pointer text-sm font-medium">
Error Details (Development)
</summary>
<pre className="mt-2 text-xs text-muted-foreground overflow-auto">
{error.message}
{error.stack && `\n\n${error.stack}`}
</pre>
</details>
)}
</div>
</div>
);
}
================================================
FILE: src/app/categories/page.tsx
================================================
"use client";
import { PageHeader } from "@/components/layout/page-header";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { fetchAndParseReadme, Resource } from "@/hooks/use-readme";
import { categoryNameToSlug } from "@/lib/slugs";
import { ArrowRight } from "lucide-react";
import { motion } from "motion/react";
import Link from "next/link";
import { useEffect, useState } from "react";
interface Category {
title: string;
items: Resource[];
description: string;
}
const CATEGORY_DESCRIPTIONS: Record<string, string> = {
"Libs and Components":
"Essential libraries and reusable components built with shadcn/ui",
Registries: "Component registries and collections for shadcn/ui",
"Plugins and Extensions":
"Tools and extensions that enhance your shadcn/ui workflow",
"Colors and Customizations":
"Themes, color palettes, and customization utilities",
Animations: "Animation libraries and motion components for shadcn/ui",
Tools: "Development tools, generators, and utilities for shadcn/ui projects",
"Websites and Portfolios Inspirations":
"Real-world examples and inspiration for your projects",
Platforms: "Platforms and services that integrate with shadcn/ui",
Ports: "Ports of shadcn/ui to other frameworks and technologies",
"Design System": "Complete design systems and component libraries",
"Boilerplates / Templates":
"Starter templates and boilerplates for quick project setup",
};
export default function CategoriesPage() {
const [categories, setCategories] = useState<Category[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
setIsLoading(true);
const fetchedResources = await fetchAndParseReadme();
const EXCLUDED_CATEGORIES = ["Star History", "Contributors"];
const groupedCategories = fetchedResources.reduce(
(acc, resource) => {
if (!EXCLUDED_CATEGORIES.includes(resource.category)) {
if (!acc[resource.category]) {
acc[resource.category] = [];
}
acc[resource.category].push(resource);
}
return acc;
},
{} as Record<string, Resource[]>,
);
const formattedCategories = Object.entries(groupedCategories).map(
([title, items]) => ({
title,
items,
description:
CATEGORY_DESCRIPTIONS[title] ||
"A collection of shadcn/ui related resources",
}),
);
setCategories(formattedCategories);
} catch (error) {
console.error("Error fetching categories:", error);
} finally {
setIsLoading(false);
}
}
fetchData();
}, []);
if (isLoading) {
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="space-y-6 sm:space-y-8">
<div className="space-y-3 sm:space-y-4">
<Skeleton className="h-8 sm:h-12 w-full max-w-96 mx-auto" />
<Skeleton className="h-4 sm:h-6 w-full max-w-64 mx-auto" />
</div>
<div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
{[...Array(9)].map((_, i) => (
<Card key={i} className="h-full">
<CardHeader className="pb-3 sm:pb-4 p-4 sm:p-6">
<div className="flex items-center justify-between gap-2">
<Skeleton className="h-5 sm:h-6 w-3/4" />
<Skeleton className="h-4 w-4 sm:h-5 sm:w-5" />
</div>
<Skeleton className="h-3 sm:h-4 w-full mt-2" />
<Skeleton className="h-3 sm:h-4 w-5/6" />
</CardHeader>
<CardContent className="pt-0 p-4 sm:p-6">
<div className="flex items-center justify-between gap-2">
<Skeleton className="h-5 sm:h-6 w-16" />
<div className="flex items-center space-x-1">
<Skeleton className="h-3 w-12" />
<Skeleton className="h-3 w-3" />
</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
</div>
);
}
return (
<motion.div
className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<PageHeader
title="Categories"
description="Explore our curated collection of shadcn/ui resources organized by category"
breadcrumbs={[{ label: "Categories", href: "/categories" }]}
/>
<div className="min-h-screen grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{categories.map((category) => (
<div key={category.title}>
<Link href={`/categories/${categoryNameToSlug(category.title)}`}>
<Card className="h-[280px] group hover:border-primary/40 hover:shadow-lg transition-all duration-200 overflow-hidden cursor-pointer flex flex-col">
<CardHeader className="p-4 pb-3">
<div className="flex items-start justify-between gap-2 mb-2">
<span className="text-[10px] font-mono uppercase tracking-wider text-primary border border-primary/30 bg-primary/5 px-2 py-0.5">
[{category.title}]
</span>
<ArrowRight className="h-4 w-4 text-muted-foreground group-hover:text-primary group-hover:translate-x-0.5 transition-all flex-shrink-0" />
</div>
<CardTitle className="text-base font-bold group-hover:text-primary transition-colors duration-200 line-clamp-2 leading-snug">
{category.title}
</CardTitle>
</CardHeader>
<div className="h-px bg-border mx-4" />
<CardContent className="px-4 py-3 flex-1 flex flex-col gap-3">
<p className="text-sm text-muted-foreground line-clamp-3 flex-1 leading-relaxed overflow-hidden">
{category.description}
</p>
</CardContent>
<div className="h-px bg-border mx-4" />
<CardFooter className="flex gap-3 items-center p-4 pt-3">
<Badge variant="secondary" className="text-xs flex-shrink-0">
{category.items.length}{" "}
{category.items.length === 1 ? "item" : "items"}
</Badge>
</CardFooter>
</Card>
</Link>
</div>
))}
</div>
</motion.div>
);
}
================================================
FILE: src/app/error.tsx
================================================
"use client";
import { Button } from "@/components/ui/button";
import { AlertTriangle, RefreshCw } from "lucide-react";
import { useEffect } from "react";
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Homepage error:", error);
}, [error]);
return (
<div className="container mx-auto max-w-7xl px-3 sm:px-4 py-4 sm:py-8">
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center space-y-6">
<div className="flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-destructive/10 dark:bg-destructive/20">
<AlertTriangle className="h-8 w-8 sm:h-10 sm:w-10 text-destructive" />
</div>
<div className="space-y-2">
<h1 className="text-2xl sm:text-3xl font-bold text-foreground">
Something went wrong
</h1>
<p className="text-sm sm:text-base text-muted-foreground max-w-md">
We encountered an error while loading the homepage. This might be a
temporary issue.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
<Button onClick={reset} className="flex items-center gap-2">
<RefreshCw className="h-4 w-4" />
Try again
</Button>
<Button
variant="outline"
onClick={() => (window.location.href = "/")}
>
Go to homepage
</Button>
</div>
{process.env.NODE_ENV === "development" && (
<details className="mt-6 p-4 bg-muted text-left max-w-2xl w-full">
<summary className="cursor-pointer text-sm font-medium">
Error Details (Development)
</summary>
<pre className="mt-2 text-xs text-muted-foreground overflow-auto">
{error.message}
{error.stack && `\n\n${error.stack}`}
</pre>
</details>
)}
</div>
</div>
);
}
================================================
FILE: src/app/global-error.tsx
================================================
"use client";
import { Button } from "@/components/ui/button";
import { AlertOctagon } from "lucide-react";
import { useEffect } from "react";
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log error
}, [error]);
return (
<html lang="en">
<body className="bg-background text-foreground">
<div className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-20 text-center">
<div className="relative z-10 mx-auto max-w-xl">
<div className="mb-6 flex justify-center">
<AlertOctagon className="size-20 text-destructive" />
</div>
<h1 className="mb-4 text-3xl font-bold tracking-tight sm:text-5xl">
Critical Error
</h1>
<p className="mb-8 text-base text-muted-foreground sm:text-lg">
We've encountered a critical application error.
<br className="hidden sm:block" />
Our team has been automatically notified and is working to resolve
the issue.
</p>
<div className="flex flex-col sm:flex-row justify-center gap-4">
<Button onClick={() => reset()} size="lg" variant="default">
Reload Application
</Button>
<Button asChild size="lg" variant="outline">
<a href="/">Return Home</a>
</Button>
</div>
{error.digest && (
<p className="mt-6 text-xs text-muted-foreground">
Error ID: {error.digest}
</p>
)}
</div>
</div>
</body>
</html>
);
}
================================================
FILE: src/app/globals.css
================================================
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@custom-variant dark (&:is(.dark *));
:root {
--card: oklch(1 0 0);
--card-foreground: oklch(0.141 0.005 285.823);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.141 0.005 285.823);
--primary: oklch(0.21 0.006 285.885);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.967 0.001 286.375);
--muted-foreground: oklch(0.552 0.016 285.938);
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(1 0 0);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.92 0.004 286.32);
--ring: oklch(0.705 0.015 286.067);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.21 0.006 285.885);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.967 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.705 0.015 286.067);
/* Custom colors for donation button */
--donation-bg: oklch(0.95 0.15 95);
--donation-bg-hover: oklch(0.92 0.18 95);
--donation-text: oklch(0.15 0 0);
--font-sans: Geist, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Geist Mono, monospace;
--radius: 0.625rem;
--shadow-x: 0px;
--shadow-y: 1px;
--shadow-blur: 2px;
--shadow-spread: 0px;
--shadow-opacity: 0.18;
--shadow-color: hsl(0 0% 0%);
--shadow-2xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09);
--shadow-xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09);
--shadow-sm:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18);
--shadow:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18);
--shadow-md:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 2px 4px -1px hsl(0 0% 0% / 0.18);
--shadow-lg:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 4px 6px -1px hsl(0 0% 0% / 0.18);
--shadow-xl:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 8px 10px -1px hsl(0 0% 0% / 0.18);
--shadow-2xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.45);
--tracking-normal: 0em;
--spacing: 0.25rem;
--background: oklch(1 0 0);
--foreground: oklch(0.141 0.005 285.823);
}
.dark {
--background: oklch(0.141 0.005 285.823);
--foreground: oklch(0.985 0 0);
--card: oklch(0.21 0.006 285.885);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.21 0.006 285.885);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.92 0.004 286.32);
--primary-foreground: oklch(0.21 0.006 285.885);
--secondary: oklch(0.274 0.006 286.033);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.274 0.006 286.033);
--muted-foreground: oklch(0.705 0.015 286.067);
--accent: oklch(0.274 0.006 286.033);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.552 0.016 285.938);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.006 285.885);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.274 0.006 286.033);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.552 0.016 285.938);
/* Custom colors for donation button */
--donation-bg: oklch(0.95 0.15 95);
--donation-bg-hover: oklch(0.92 0.18 95);
--donation-text: oklch(0.15 0 0);
--font-sans: Geist, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Geist Mono, monospace;
--radius: 0.5rem;
--shadow-x: 0px;
--shadow-y: 1px;
--shadow-blur: 2px;
--shadow-spread: 0px;
--shadow-opacity: 0.18;
--shadow-color: hsl(0 0% 0%);
--shadow-2xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09);
--shadow-xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09);
--shadow-sm:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18);
--shadow:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18);
--shadow-md:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 2px 4px -1px hsl(0 0% 0% / 0.18);
--shadow-lg:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 4px 6px -1px hsl(0 0% 0% / 0.18);
--shadow-xl:
0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 8px 10px -1px hsl(0 0% 0% / 0.18);
--shadow-2xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.45);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-donation-bg: var(--donation-bg);
--color-donation-bg-hover: var(--donation-bg-hover);
--color-donation-text: var(--donation-text);
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--font-serif: var(--font-serif);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--shadow-2xs: var(--shadow-2xs);
--shadow-xs: var(--shadow-xs);
--shadow-sm: var(--shadow-sm);
--shadow: var(--shadow);
--shadow-md: var(--shadow-md);
--shadow-lg: var(--shadow-lg);
--shadow-xl: var(--shadow-xl);
--shadow-2xl: var(--shadow-2xl);
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
--tracking-normal: var(--tracking-normal);
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
--animate-marquee-left: marquee-left var(--marquee-duration) linear var(--marquee-loop-count);
--animate-marquee-right: marquee-right var(--marquee-duration) linear var(--marquee-loop-count);
--animate-marquee-left-rtl: marquee-left-rtl var(--marquee-duration) linear var(--marquee-loop-count);
--animate-marquee-right-rtl: marquee-right-rtl var(--marquee-duration) linear var(--marquee-loop-count);
--animate-marquee-up: marquee-up var(--marquee-duration) linear var(--marquee-loop-count);
--animate-marquee-down: marquee-down var(--marquee-duration) linear var(--marquee-loop-count);
@keyframes marquee-left {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(calc(-100% - var(--marquee-gap)));
}
}
@keyframes marquee-right {
0% {
transform: translateX(calc(-100% - var(--marquee-gap)));
}
100% {
transform: translateX(0%);
}
}
@keyframes marquee-up {
0% {
transform: translateY(0%);
}
100% {
transform: translateY(calc(-100% - var(--marquee-gap)));
}
}
@keyframes marquee-down {
0% {
transform: translateY(calc(-100% - var(--marquee-gap)));
}
100% {
transform: translateY(0%);
}
}
@keyframes marquee-left-rtl {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(calc(100% + var(--marquee-gap)));
}
}
@keyframes marquee-right-rtl {
0% {
transform: translateX(calc(100% + var(--marquee-gap)));
}
100% {
transform: translateX(0%);
}
}
}
body {
letter-spacing: var(--tracking-normal);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
html {
scroll-behavior: smooth;
}
body {
@apply bg-background text-foreground;
}
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.font-mono {
font-family: var(--font-mono);
}
.text-terminal {
font-family: var(--font-mono);
letter-spacing: -0.02em;
}
}
================================================
FILE: src/app/layout.tsx
================================================
import { Footer } from "@/components/layout/footer";
import { Header } from "@/components/layout/header";
import { Providers } from "@/providers/providers";
import { type Metadata, type Viewport } from "next";
import { GoogleAnalytics } from "@next/third-parties/google";
import Script from "next/script";
import { TooltipProvider } from "@/components/ui/tooltip";
import type React from "react";
import { Suspense } from "react";
import { cn } from "@/lib/utils";
import "@/app/globals.css";
import { Geist, Geist_Mono } from "next/font/google";
export const metadata: Metadata = {
metadataBase: new URL("https://awesome-shadcn-ui.vercel.app"),
title: {
default: "awesome-shadcn-ui",
template: `%s | awesome-shadcn-ui`,
},
description: "A curated list of awesome things related to shadcn/ui",
keywords: [
"shadcn",
"ui library",
"awesome",
"github",
"readme",
"shad",
"awesome list",
"awesome shad",
"shadcn ui",
],
alternates: {
canonical: "https://awesome-shadcn-ui.vercel.app/",
},
openGraph: {
type: "website",
locale: "en_US",
url: "https://awesome-shadcn-ui.vercel.app/",
siteName: "awesome-shadcn-ui",
title: "awesome-shadcn-ui",
description: "A curated list of awesome things related to shadcn/ui",
images: [
{
url: "/seo.png",
width: 1200,
height: 630,
alt: "awesome-shadcn-ui",
},
],
},
twitter: {
creator: "@birobirobiro",
site: "@birobirobiro",
card: "summary_large_image",
title: "awesome-shadcn-ui",
description: "A curated list of awesome things related to shadcn/ui",
images: ["/seo.png"],
},
icons: {
icon: "/favicon.ico",
},
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 1,
userScalable: false,
};
const geistSans = Geist({
variable: "--font-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-mono",
subsets: ["latin"],
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
geistSans.variable,
geistMono.variable,
)}
>
<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_ID || ""} />
{process.env.HIMETRICA_API_KEY && (
<Script
src="https://cdn.himetrica.com/tracker.js"
data-api-key={process.env.HIMETRICA_API_KEY}
strategy="afterInteractive"
/>
)}
<Providers>
<TooltipProvider>
<Suspense>
<div className="relative flex min-h-screen flex-col">
<Header />
<main className="flex-1">{children}</main>
<Footer />
</div>
</Suspense>
</TooltipProvider>
</Providers>
</body>
</html>
);
}
================================================
FILE: src/app/not-found.tsx
================================================
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function NotFound() {
return (
<div className="relative flex flex-1 flex-col items-center justify-center overflow-hidden p-4 text-center">
<svg
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-20 z-0 h-auto w-full max-w-5xl mx-auto text-foreground opacity-[0.04] dark:opacity-[0.03]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 362 145"
>
<path
fill="currentColor"
d="M62.6 142c-2.133 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2L58.2 4c.8-1.333 2.067-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .667.533 1 1.267 1 2.2v21.2c0 .933-.333 1.733-1 2.4-.667.533-1.467.8-2.4.8H93v20.8c0 2.133-1.067 3.2-3.2 3.2H62.6zM33 90.4h26.4V51.2L33 90.4zM181.67 144.6c-7.333 0-14.333-1.333-21-4-6.666-2.667-12.866-6.733-18.6-12.2-5.733-5.467-10.266-13-13.6-22.6-3.333-9.6-5-20.667-5-33.2 0-12.533 1.667-23.6 5-33.2 3.334-9.6 7.867-17.133 13.6-22.6 5.734-5.467 11.934-9.533 18.6-12.2 6.667-2.8 13.667-4.2 21-4.2 7.467 0 14.534 1.4 21.2 4.2 6.667 2.667 12.8 6.733 18.4 12.2 5.734 5.467 10.267 13 13.6 22.6 3.334 9.6 5 20.667 5 33.2 0 12.533-1.666 23.6-5 33.2-3.333 9.6-7.866 17.133-13.6 22.6-5.6 5.467-11.733 9.533-18.4 12.2-6.666 2.667-13.733 4-21.2 4zm0-31c9.067 0 15.6-3.733 19.6-11.2 4.134-7.6 6.2-17.533 6.2-29.8s-2.066-22.2-6.2-29.8c-4.133-7.6-10.666-11.4-19.6-11.4-8.933 0-15.466 3.8-19.6 11.4-4 7.6-6 17.533-6 29.8s2 22.2 6 29.8c4.134 7.467 10.667 11.2 19.6 11.2zM316.116 142c-2.134 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2l56.6-84.6c.8-1.333 2.066-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .666.533 1 1.267 1 2.2v21.2c0 .933-.334 1.733-1 2.4-.667.533-1.467.8-2.4.8h-11.2v20.8c0 2.133-1.067 3.2-3.2 3.2h-27.2zm-29.6-51.6h26.4V51.2l-26.4 39.2z"
/>
</svg>
<div className="relative sm:mt-36 z-10 mx-auto max-w-xl">
<h1 className="mb-4 text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
Page Not Found
</h1>
<p className="mb-8 text-base text-muted-foreground sm:text-lg">
Sorry, we couldn't find the page you're looking for.
<br className="hidden sm:block" />
It might have been moved, deleted, or the URL might be incorrect.
</p>
<Button asChild size="lg">
<Link href="/">Return Home</Link>
</Button>
</div>
</div>
);
}
================================================
FILE: src/app/page.tsx
================================================
"use client";
import { SubmitCTA } from "@/components/sections/cta-submit";
import Hero from "@/components/sections/hero";
import ItemList from "@/components/sections/items-list";
import { Resource, fetchAndParseReadme } from "@/hooks/use-readme";
import { isValid, parseISO } from "date-fns";
import { motion } from "motion/react";
import { useEffect, useState } from "react";
interface Category {
title: string;
items: Resource[];
}
const EXCLUDED_CATEGORIES = ["Star History", "Contributors"];
export default function Home() {
const [categories, setCategories] = useState<Category[]>([]);
const [filteredItems, setFilteredItems] = useState<Resource[]>([]);
useEffect(() => {
async function fetchData() {
const fetchedResources = await fetchAndParseReadme();
const groupedCategories = fetchedResources.reduce(
(acc, resource) => {
if (!EXCLUDED_CATEGORIES.includes(resource.category)) {
if (!acc[resource.category]) {
acc[resource.category] = [];
}
acc[resource.category].push(resource);
}
return acc;
},
{} as Record<string, Resource[]>,
);
const formattedCategories = Object.entries(groupedCategories).map(
([title, items]) => ({
title,
items,
}),
);
const eligibleItems = fetchedResources.filter(
(item) => !EXCLUDED_CATEGORIES.includes(item.category),
);
const sortedItems = eligibleItems.sort((a, b) => {
const dateA =
a.date && a.date !== "Unknown" ? parseISO(a.date) : new Date(0);
const dateB =
b.date && b.date !== "Unknown" ? parseISO(b.date) : new Date(0);
if (!isValid(dateA)) return 1;
if (!isValid(dateB)) return -1;
return dateB.getTime() - dateA.getTime();
});
setCategories(formattedCategories);
setFilteredItems(sortedItems);
}
fetchData();
}, []);
return (
<motion.div
className="container mx-auto max-w-7xl px-4"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<Hero />
<motion.div
className="mt-12 mb-8"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<ItemList
key={filteredItems.length}
items={filteredItems}
categories={categories}
/>
</motion.div>
<SubmitCTA />
</motion.div>
);
}
================================================
FILE: src/components/github-stars.tsx
================================================
"use client";
import { Octokit } from "@octokit/rest";
import { useEffect, useState } from "react";
async function getStars() {
const octokit = new Octokit();
const { data } = await octokit.repos.get({
owner: "birobirobiro",
repo: "awesome-shadcn-ui",
});
return data.stargazers_count;
}
export function GithubStars() {
const [stars, setStars] = useState<number | null>(null);
useEffect(() => {
getStars().then(setStars);
}, []);
if (stars === null) return null;
const formattedStars =
stars >= 1000 ? `${(stars / 1000).toFixed(1)}k` : stars;
return <span className="text-sm">{formattedStars}</span>;
}
================================================
FILE: src/components/item-card.tsx
================================================
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { ArrowRight, Bookmark, ExternalLink } from "lucide-react";
import { Button } from "@/components/ui/button";
import { categoryNameToSlug } from "@/lib/slugs";
import { cn } from "@/lib/utils";
import { motion } from "motion/react";
import React from "react";
interface ItemCardProps {
id: string;
title: string;
description: string;
url: string;
category: string;
date?: string;
isBookmarked: boolean;
onBookmark: (id: string) => void;
isBookmarkLoading?: boolean;
}
const standardAnimations = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -20 },
transition: { duration: 0.2, ease: "easeOut" },
};
const ItemCard: React.FC<ItemCardProps> = ({
id,
title,
description,
url,
category,
date,
isBookmarked,
onBookmark,
isBookmarkLoading = false,
}) => {
const handleCardClick = () => {
window.location.href = `/categories/${categoryNameToSlug(category)}/${id}`;
};
return (
<motion.div layout {...standardAnimations} className="h-full">
<Card
className="h-full group hover:border-primary/40 hover:shadow-lg transition-all duration-200 overflow-hidden cursor-pointer flex flex-col gap-0 py-0"
onClick={handleCardClick}
>
<CardHeader className="p-4 pb-3 shrink-0">
<div className="flex items-start justify-between gap-2 mb-2">
<span className="text-[10px] font-mono uppercase tracking-wider text-primary border border-primary/30 bg-primary/5 px-2 py-0.5">
[{category}]
</span>
<ArrowRight className="h-4 w-4 text-muted-foreground group-hover:text-primary group-hover:translate-x-0.5 transition-all flex-shrink-0" />
</div>
<CardTitle className="text-base font-bold group-hover:text-primary transition-colors duration-200 line-clamp-2 leading-snug">
{title}
</CardTitle>
</CardHeader>
<div className="h-px bg-border mx-4 shrink-0" />
<CardContent className="px-4 py-3 flex-1 flex flex-col gap-3 min-h-0">
<p className="text-sm text-muted-foreground line-clamp-3 flex-1 leading-relaxed overflow-hidden">
{description}
</p>
{date && (
<div className="flex items-center gap-2 text-xs font-mono text-muted-foreground/70">
<span>Added: {date}</span>
</div>
)}
</CardContent>
<div className="h-px bg-border mx-4 shrink-0" />
<CardFooter className="flex gap-3 items-center p-4 pt-3 shrink-0">
{/* Bookmark Button */}
<Button
variant={isBookmarked ? "default" : "ghost"}
size="icon"
disabled={isBookmarkLoading}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (!isBookmarkLoading) onBookmark(id);
}}
className={cn(
"h-8 w-8 transition-all duration-200",
isBookmarked
? "bg-primary hover:bg-primary/90 text-primary-foreground"
: "text-muted-foreground hover:text-foreground hover:bg-muted",
isBookmarkLoading && "opacity-50 cursor-not-allowed",
)}
>
<Bookmark
className={cn(
"h-3.5 w-3.5",
isBookmarked && "fill-current",
isBookmarkLoading && "animate-pulse",
)}
/>
</Button>
{/* External Link Button */}
<Button
variant="ghost"
size="icon"
asChild
className="h-8 w-8 text-muted-foreground hover:text-foreground hover:bg-muted transition-all duration-200"
>
<a
href={url}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
>
<ExternalLink className="h-3.5 w-3.5" />
</a>
</Button>
</CardFooter>
</Card>
</motion.div>
);
};
export default ItemCard;
================================================
FILE: src/components/item-grid.tsx
================================================
import React from "react";
import { Resource } from "@/hooks/use-readme";
import { AnimatePresence, LayoutGroup, motion } from "motion/react";
import ItemCard from "./item-card";
import SponsorCard from "./sponsor-card";
import { sponsors } from "@/components/sponsors/sponsors";
const SPONSOR_INTERVAL = 8;
interface ItemGridProps {
items: Resource[];
bookmarkedItems: string[];
onBookmark: (id: string) => void;
isBookmarkLoading?: boolean;
}
export function ItemGrid({
items,
bookmarkedItems,
onBookmark,
isBookmarkLoading = false,
}: ItemGridProps) {
if (items.length === 0) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2 }}
className="text-center py-12 border border-dashed border-border bg-muted/30"
>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
className="inline-flex justify-center items-center w-16 h-16 bg-muted mb-4"
>
<svg
className="w-8 h-8 text-muted-foreground"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</motion.div>
<p className="text-foreground text-lg font-medium">
No items found matching your criteria.
</p>
<p className="text-muted-foreground text-sm mt-1">
Try adjusting your search or filter settings.
</p>
</motion.div>
);
}
const gridItems: React.ReactNode[] = [];
items.forEach((item, index) => {
gridItems.push(
<ItemCard
key={`item-${item.id}`}
id={item.id}
title={item.name}
description={item.description}
url={item.url}
category={item.category}
date={item.date}
isBookmarked={bookmarkedItems.includes(item.id)}
onBookmark={onBookmark}
isBookmarkLoading={isBookmarkLoading}
/>
);
if (index % SPONSOR_INTERVAL === 0 && sponsors.length > 0) {
const sponsor = sponsors[Math.floor(index / SPONSOR_INTERVAL) % sponsors.length];
gridItems.push(
<SponsorCard key={`sponsor-${index}`} sponsor={sponsor} />
);
}
});
return (
<LayoutGroup>
<AnimatePresence mode="wait">
<motion.div
key={`grid`}
className={`grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
layout
>
{gridItems}
</motion.div>
</AnimatePresence>
</LayoutGroup>
);
}
================================================
FILE: src/components/kibo-ui/theme-switcher/index.tsx
================================================
"use client";
import { useTheme } from "next-themes";
import { Monitor, Moon, Sun } from "lucide-react";
import { motion } from "motion/react";
import { useCallback, useEffect, useState } from "react";
import { cn } from "@/lib/utils";
const themes = [
{
key: "system",
icon: Monitor,
label: "System theme",
},
{
key: "light",
icon: Sun,
label: "Light theme",
},
{
key: "dark",
icon: Moon,
label: "Dark theme",
},
];
export type ThemeSwitcherProps = {
value?: "light" | "dark" | "system";
onChange?: (theme: "light" | "dark" | "system") => void;
defaultValue?: "light" | "dark" | "system";
className?: string;
};
export const ThemeSwitcher = ({
value,
onChange,
defaultValue = "system",
className,
}: ThemeSwitcherProps) => {
const { theme, setTheme, resolvedTheme } = useTheme();
const [mounted, setMounted] = useState(false);
const handleThemeClick = useCallback(
(themeKey: "light" | "dark" | "system") => {
setTheme(themeKey);
onChange?.(themeKey);
},
[setTheme, onChange],
);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
const currentTheme = value || theme || defaultValue;
return (
<div
className={cn(
"relative isolate flex h-8 rounded-full bg-background p-1",
className,
)}
>
{themes.map(({ key, icon: Icon, label }) => {
const isActive = currentTheme === key;
return (
<button
aria-label={label}
className="relative h-6 w-6 rounded-full"
key={key}
onClick={() => handleThemeClick(key as "light" | "dark" | "system")}
type="button"
>
{isActive && (
<motion.div
className="absolute inset-0 rounded-full bg-secondary"
layoutId="activeTheme"
transition={{ type: "spring", duration: 0.5 }}
/>
)}
<Icon
className={cn(
"relative z-10 m-auto h-4 w-4",
isActive ? "text-foreground" : "text-muted-foreground",
)}
/>
</button>
);
})}
</div>
);
};
================================================
FILE: src/components/layout/footer.tsx
================================================
"use client";
import { Separator } from "@/components/ui/separator";
import { ThemeSwitcher } from "@/components/kibo-ui/theme-switcher";
import { useCategories } from "@/hooks/use-categories";
import { ArrowRight, ExternalLink, Github } from "lucide-react";
import { motion, useInView } from "motion/react";
import Link from "next/link";
import { useRef } from "react";
import { Button } from "../ui/button";
import { GithubStars } from "@/components/github-stars";
export function Footer() {
const { categories } = useCategories();
const ref = useRef(null);
const isInView = useInView(ref, { once: true, amount: 0.3 });
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
const quickLinks = [
{ name: "Home", href: "/" },
{ name: "Categories", href: "/categories" },
{
name: "GitHub",
href: "https://github.com/birobirobiro/awesome-shadcn-ui",
external: true,
},
];
const socialLinks = [
{
name: "GitHub",
href: "https://github.com/birobirobiro/awesome-shadcn-ui",
icon: Github,
},
];
return (
<footer className="border-t bg-background">
<motion.div
ref={ref}
variants={containerVariants}
initial="hidden"
animate={isInView ? "visible" : "hidden"}
className="container mx-auto px-4 py-6 md:py-8"
>
{/* Main Footer Content */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-6">
{/* Brand Section */}
<motion.div variants={itemVariants} className="lg:col-span-1">
<Link href="/" className="flex items-center space-x-2 mb-4 group">
<img
src="/logo.svg"
alt="logo"
className="h-8 w-auto transition-transform group-hover:scale-105"
/>
<span className="text-lg font-bold">awesome-shadcn/ui</span>
</Link>
<p className="text-sm text-muted-foreground mb-4 max-w-sm">
A curated list of awesome things related to shadcn/ui. Discover,
contribute, and grow the community.
</p>
</motion.div>
{/* Quick Links */}
<motion.div variants={itemVariants}>
<h4 className="font-semibold mb-4">Quick Links</h4>
<ul className="space-y-2">
{quickLinks.map((link) => (
<li key={link.name}>
<Link
href={link.href}
target={link.external ? "_blank" : undefined}
rel={link.external ? "noopener noreferrer" : undefined}
className="text-sm text-muted-foreground hover:text-foreground transition-colors flex items-center gap-2 group"
>
{link.name}
{link.external && (
<ExternalLink className="h-3 w-3 group-hover:translate-x-0.5 transition-transform" />
)}
</Link>
</li>
))}
</ul>
</motion.div>
{/* Categories Preview */}
<motion.div variants={itemVariants}>
<h4 className="font-semibold mb-4">Categories</h4>
<ul className="space-y-2">
{categories.slice(0, 6).map((category) => (
<li key={category.title}>
<Link
href={`/categories/${category.slug}`}
className="text-sm text-muted-foreground hover:text-foreground transition-colors flex items-center gap-2 group"
>
{category.title}
<ArrowRight className="h-3 w-3 group-hover:translate-x-0.5 transition-tra
gitextract_2m4yzxwy/ ├── .github/ │ ├── FUNDING.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── add-dates.yml │ └── format-readme.yml ├── .gitignore ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── components.json ├── next.config.mjs ├── open-next.config.ts ├── package.json ├── postcss.config.mjs ├── scripts/ │ ├── add-dates.js │ └── format-readme.js ├── src/ │ ├── app/ │ │ ├── [...not-found]/ │ │ │ └── page.tsx │ │ ├── api/ │ │ │ ├── github/ │ │ │ │ ├── callback/ │ │ │ │ │ └── route.ts │ │ │ │ └── device-flow/ │ │ │ │ └── route.ts │ │ │ └── submit-resource/ │ │ │ └── route.ts │ │ ├── bookmarks/ │ │ │ ├── error.tsx │ │ │ └── page.tsx │ │ ├── categories/ │ │ │ ├── [category]/ │ │ │ │ ├── [id]/ │ │ │ │ │ ├── error.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── error.tsx │ │ │ │ └── page.tsx │ │ │ ├── error.tsx │ │ │ └── page.tsx │ │ ├── error.tsx │ │ ├── global-error.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── not-found.tsx │ │ └── page.tsx │ ├── components/ │ │ ├── github-stars.tsx │ │ ├── item-card.tsx │ │ ├── item-grid.tsx │ │ ├── kibo-ui/ │ │ │ └── theme-switcher/ │ │ │ └── index.tsx │ │ ├── layout/ │ │ │ ├── footer.tsx │ │ │ ├── header.tsx │ │ │ └── page-header.tsx │ │ ├── logo.tsx │ │ ├── pagination-controls.tsx │ │ ├── pr-submission-dialog.tsx │ │ ├── search-filter-controls.tsx │ │ ├── sections/ │ │ │ ├── cta-submit.tsx │ │ │ ├── hero.tsx │ │ │ ├── items-list.tsx │ │ │ └── sponsorship.tsx │ │ ├── sort.tsx │ │ ├── sponsor-card.tsx │ │ ├── sponsors/ │ │ │ ├── shadcn-blocks-logo.tsx │ │ │ ├── shadcn-studio-logo.tsx │ │ │ ├── shadcn-ui-kit-logo.tsx │ │ │ └── sponsors.tsx │ │ ├── theme-toggle.tsx │ │ └── ui/ │ │ ├── accordion.tsx │ │ ├── alert-dialog.tsx │ │ ├── alert.tsx │ │ ├── aspect-ratio.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── breadcrumb.tsx │ │ ├── button-group.tsx │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── card.tsx │ │ ├── carousel.tsx │ │ ├── chart.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── combobox.tsx │ │ ├── command.tsx │ │ ├── context-menu.tsx │ │ ├── dialog.tsx │ │ ├── direction.tsx │ │ ├── drawer.tsx │ │ ├── dropdown-menu.tsx │ │ ├── empty.tsx │ │ ├── field.tsx │ │ ├── hover-card.tsx │ │ ├── input-group.tsx │ │ ├── input-otp.tsx │ │ ├── input.tsx │ │ ├── item.tsx │ │ ├── kbd.tsx │ │ ├── label.tsx │ │ ├── marquee.tsx │ │ ├── menubar.tsx │ │ ├── multi-select.tsx │ │ ├── native-select.tsx │ │ ├── navigation-menu.tsx │ │ ├── pagination.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── resizable.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── sonner.tsx │ │ ├── spinner.tsx │ │ ├── switch.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toggle-group.tsx │ │ ├── toggle.tsx │ │ └── tooltip.tsx │ ├── hooks/ │ │ ├── use-bookmark.ts │ │ ├── use-categories.ts │ │ ├── use-debounce.ts │ │ ├── use-github-auth.ts │ │ ├── use-mobile.ts │ │ ├── use-pr-submission.ts │ │ ├── use-readme.ts │ │ └── use-website-preview.ts │ ├── lib/ │ │ ├── compose-refs.ts │ │ ├── config.ts │ │ ├── slugs.ts │ │ └── utils.ts │ └── providers/ │ ├── providers.tsx │ └── theme-provider.tsx ├── tsconfig.json └── wrangler.jsonc
SYMBOL INDEX (448 symbols across 110 files)
FILE: scripts/add-dates.js
function isValidDate (line 20) | function isValidDate(dateString) {
FILE: src/app/[...not-found]/page.tsx
function CatchAllNotFound (line 3) | function CatchAllNotFound() {
FILE: src/app/api/github/callback/route.ts
function GET (line 3) | async function GET(request: NextRequest) {
FILE: src/app/api/github/device-flow/route.ts
function POST (line 4) | async function POST(request: NextRequest) {
FILE: src/app/api/submit-resource/route.ts
constant GITHUB_CONFIG (line 4) | const GITHUB_CONFIG = {
type SubmissionData (line 10) | interface SubmissionData {
function insertResourceIntoReadme (line 17) | function insertResourceIntoReadme(
function generatePRBody (line 107) | function generatePRBody(submission: SubmissionData): string {
function POST (line 135) | async function POST(request: NextRequest) {
FILE: src/app/bookmarks/error.tsx
function BookmarksError (line 8) | function BookmarksError({
FILE: src/app/bookmarks/page.tsx
constant ITEMS_PER_PAGE_OPTIONS (line 25) | const ITEMS_PER_PAGE_OPTIONS = [20, 40, 60, 80];
function BookmarksPage (line 27) | function BookmarksPage() {
FILE: src/app/categories/[category]/[id]/error.tsx
function ItemError (line 8) | function ItemError({
FILE: src/app/categories/[category]/[id]/page.tsx
type ItemPageProps (line 24) | interface ItemPageProps {
constant CATEGORY_DESCRIPTIONS (line 31) | const CATEGORY_DESCRIPTIONS: Record<string, string> = {
function ItemPage (line 50) | function ItemPage({ params }: ItemPageProps) {
FILE: src/app/categories/[category]/error.tsx
function CategoryError (line 8) | function CategoryError({
FILE: src/app/categories/[category]/page.tsx
type CategoryPageProps (line 16) | interface CategoryPageProps {
constant CATEGORY_DESCRIPTIONS (line 22) | const CATEGORY_DESCRIPTIONS: Record<string, string> = {
constant ITEMS_PER_PAGE_OPTIONS (line 41) | const ITEMS_PER_PAGE_OPTIONS = [20, 40, 60, 80];
function CategoryPage (line 43) | function CategoryPage({ params }: CategoryPageProps) {
FILE: src/app/categories/error.tsx
function CategoriesError (line 8) | function CategoriesError({
FILE: src/app/categories/page.tsx
type Category (line 21) | interface Category {
constant CATEGORY_DESCRIPTIONS (line 27) | const CATEGORY_DESCRIPTIONS: Record<string, string> = {
function CategoriesPage (line 46) | function CategoriesPage() {
FILE: src/app/error.tsx
function Error (line 7) | function Error({
FILE: src/app/global-error.tsx
function GlobalError (line 7) | function GlobalError({
FILE: src/app/layout.tsx
function RootLayout (line 83) | function RootLayout({
FILE: src/app/not-found.tsx
function NotFound (line 4) | function NotFound() {
FILE: src/app/page.tsx
type Category (line 11) | interface Category {
constant EXCLUDED_CATEGORIES (line 16) | const EXCLUDED_CATEGORIES = ["Star History", "Contributors"];
function Home (line 18) | function Home() {
FILE: src/components/github-stars.tsx
function getStars (line 6) | async function getStars() {
function GithubStars (line 15) | function GithubStars() {
FILE: src/components/item-card.tsx
type ItemCardProps (line 16) | interface ItemCardProps {
FILE: src/components/item-grid.tsx
constant SPONSOR_INTERVAL (line 8) | const SPONSOR_INTERVAL = 8;
type ItemGridProps (line 10) | interface ItemGridProps {
function ItemGrid (line 17) | function ItemGrid({
FILE: src/components/kibo-ui/theme-switcher/index.tsx
type ThemeSwitcherProps (line 27) | type ThemeSwitcherProps = {
FILE: src/components/layout/footer.tsx
function Footer (line 13) | function Footer() {
FILE: src/components/layout/header.tsx
function Header (line 27) | function Header() {
FILE: src/components/layout/page-header.tsx
type PageHeaderProps (line 15) | interface PageHeaderProps {
function PageHeader (line 27) | function PageHeader({
FILE: src/components/logo.tsx
type LogoProps (line 3) | interface LogoProps {
function Logo (line 7) | function Logo({ className = "h-5 w-auto" }: LogoProps) {
function HeroLogo (line 19) | function HeroLogo({ className = "h-40 w-auto" }: LogoProps) {
FILE: src/components/pagination-controls.tsx
type PaginationControlsProps (line 18) | interface PaginationControlsProps {
function PaginationControls (line 27) | function PaginationControls({
FILE: src/components/pr-submission-dialog.tsx
constant CATEGORIES (line 33) | const CATEGORIES = [
type PRSubmissionDialogProps (line 47) | interface PRSubmissionDialogProps {
function PRSubmissionDialog (line 51) | function PRSubmissionDialog({ trigger }: PRSubmissionDialogProps) {
FILE: src/components/search-filter-controls.tsx
type SearchFilterControlsProps (line 11) | interface SearchFilterControlsProps {
function SearchFilterControls (line 21) | function SearchFilterControls({
FILE: src/components/sections/cta-submit.tsx
function SubmitCTA (line 11) | function SubmitCTA() {
FILE: src/components/sections/hero.tsx
function Hero (line 30) | function Hero() {
FILE: src/components/sections/items-list.tsx
constant ITEMS_PER_PAGE_OPTIONS (line 17) | const ITEMS_PER_PAGE_OPTIONS = [20, 40, 60, 80];
type Category (line 19) | interface Category {
type ItemListProps (line 24) | interface ItemListProps {
function ItemList (line 29) | function ItemList({
FILE: src/components/sections/sponsorship.tsx
function Sponsorship (line 19) | function Sponsorship() {
FILE: src/components/sort.tsx
type SortOption (line 11) | type SortOption = "date-desc" | "date-asc" | "name-asc" | "name-desc";
type SortProps (line 13) | interface SortProps {
function Sort (line 19) | function Sort({ sortOption, onSortChange, className }: SortProps) {
FILE: src/components/sponsor-card.tsx
type SponsorCardProps (line 22) | interface SponsorCardProps {
FILE: src/components/sponsors/shadcn-blocks-logo.tsx
type ShadcnBlocksLogoProps (line 3) | interface ShadcnBlocksLogoProps {
function ShadcnBlocksLogo (line 7) | function ShadcnBlocksLogo({ className = "w-5 h-5" }: ShadcnBlocksLogoPro...
FILE: src/components/sponsors/shadcn-studio-logo.tsx
type ShadcnStudioLogoProps (line 3) | interface ShadcnStudioLogoProps {
function ShadcnStudioLogo (line 7) | function ShadcnStudioLogo({ className = "w-5 h-5" }: ShadcnStudioLogoPro...
FILE: src/components/sponsors/shadcn-ui-kit-logo.tsx
type ShadcnUiKitLogoProps (line 4) | interface ShadcnUiKitLogoProps {
function ShadcnUiKitLogo (line 8) | function ShadcnUiKitLogo({ className = "w-5 h-5" }: ShadcnUiKitLogoProps) {
FILE: src/components/sponsors/sponsors.tsx
type Sponsor (line 6) | interface Sponsor {
FILE: src/components/theme-toggle.tsx
function ModeToggle (line 15) | function ModeToggle() {
FILE: src/components/ui/accordion.tsx
function Accordion (line 9) | function Accordion({
function AccordionItem (line 22) | function AccordionItem({
function AccordionTrigger (line 35) | function AccordionTrigger({
function AccordionContent (line 58) | function AccordionContent({
FILE: src/components/ui/alert-dialog.tsx
function AlertDialog (line 9) | function AlertDialog({
function AlertDialogTrigger (line 15) | function AlertDialogTrigger({
function AlertDialogPortal (line 23) | function AlertDialogPortal({
function AlertDialogOverlay (line 31) | function AlertDialogOverlay({
function AlertDialogContent (line 47) | function AlertDialogContent({
function AlertDialogHeader (line 70) | function AlertDialogHeader({
function AlertDialogFooter (line 86) | function AlertDialogFooter({
function AlertDialogMedia (line 102) | function AlertDialogMedia({
function AlertDialogTitle (line 118) | function AlertDialogTitle({
function AlertDialogDescription (line 134) | function AlertDialogDescription({
function AlertDialogAction (line 150) | function AlertDialogAction({
function AlertDialogCancel (line 168) | function AlertDialogCancel({
FILE: src/components/ui/alert.tsx
function Alert (line 22) | function Alert({
function AlertTitle (line 37) | function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
function AlertDescription (line 50) | function AlertDescription({
function AlertAction (line 66) | function AlertAction({ className, ...props }: React.ComponentProps<"div"...
FILE: src/components/ui/aspect-ratio.tsx
function AspectRatio (line 5) | function AspectRatio({
FILE: src/components/ui/avatar.tsx
function Avatar (line 8) | function Avatar({
function AvatarImage (line 28) | function AvatarImage({
function AvatarFallback (line 44) | function AvatarFallback({
function AvatarBadge (line 60) | function AvatarBadge({ className, ...props }: React.ComponentProps<"span...
function AvatarGroup (line 76) | function AvatarGroup({ className, ...props }: React.ComponentProps<"div"...
function AvatarGroupCount (line 89) | function AvatarGroupCount({
FILE: src/components/ui/badge.tsx
function Badge (line 30) | function Badge({
FILE: src/components/ui/breadcrumb.tsx
function Breadcrumb (line 7) | function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
function BreadcrumbList (line 18) | function BreadcrumbList({ className, ...props }: React.ComponentProps<"o...
function BreadcrumbItem (line 31) | function BreadcrumbItem({ className, ...props }: React.ComponentProps<"l...
function BreadcrumbLink (line 41) | function BreadcrumbLink({
function BreadcrumbPage (line 59) | function BreadcrumbPage({ className, ...props }: React.ComponentProps<"s...
function BreadcrumbSeparator (line 72) | function BreadcrumbSeparator({
function BreadcrumbEllipsis (line 92) | function BreadcrumbEllipsis({
FILE: src/components/ui/button-group.tsx
function ButtonGroup (line 24) | function ButtonGroup({
function ButtonGroupText (line 40) | function ButtonGroupText({
function ButtonGroupSeparator (line 60) | function ButtonGroupSeparator({
FILE: src/components/ui/button.tsx
function Button (line 42) | function Button({
FILE: src/components/ui/calendar.tsx
function Calendar (line 15) | function Calendar({
function CalendarDayButton (line 183) | function CalendarDayButton({
FILE: src/components/ui/card.tsx
function Card (line 5) | function Card({
function CardHeader (line 23) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
function CardTitle (line 36) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
function CardDescription (line 49) | function CardDescription({ className, ...props }: React.ComponentProps<"...
function CardAction (line 59) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
function CardContent (line 72) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
function CardFooter (line 82) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
FILE: src/components/ui/carousel.tsx
type CarouselApi (line 12) | type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters (line 13) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions (line 14) | type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin (line 15) | type CarouselPlugin = UseCarouselParameters[1]
type CarouselProps (line 17) | type CarouselProps = {
type CarouselContextProps (line 24) | type CarouselContextProps = {
function useCarousel (line 35) | function useCarousel() {
function Carousel (line 45) | function Carousel({
function CarouselContent (line 135) | function CarouselContent({ className, ...props }: React.ComponentProps<"...
function CarouselItem (line 156) | function CarouselItem({ className, ...props }: React.ComponentProps<"div...
function CarouselPrevious (line 174) | function CarouselPrevious({
function CarouselNext (line 204) | function CarouselNext({
FILE: src/components/ui/chart.tsx
constant THEMES (line 9) | const THEMES = { light: "", dark: ".dark" } as const
type ChartConfig (line 11) | type ChartConfig = {
type ChartContextProps (line 21) | type ChartContextProps = {
function useChart (line 27) | function useChart() {
function ChartContainer (line 37) | function ChartContainer({
function ChartTooltipContent (line 107) | function ChartTooltipContent({
function ChartLegendContent (line 255) | function ChartLegendContent({
function getPayloadConfigFromPayload (line 311) | function getPayloadConfigFromPayload(
FILE: src/components/ui/checkbox.tsx
function Checkbox (line 9) | function Checkbox({
FILE: src/components/ui/collapsible.tsx
function Collapsible (line 5) | function Collapsible({
function CollapsibleTrigger (line 11) | function CollapsibleTrigger({
function CollapsibleContent (line 22) | function CollapsibleContent({
FILE: src/components/ui/combobox.tsx
function ComboboxValue (line 18) | function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
function ComboboxTrigger (line 22) | function ComboboxTrigger({
function ComboboxClear (line 39) | function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear....
function ComboboxInput (line 52) | function ComboboxInput({
function ComboboxContent (line 89) | function ComboboxContent({
function ComboboxList (line 123) | function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Pr...
function ComboboxItem (line 136) | function ComboboxItem({
function ComboboxGroup (line 162) | function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group....
function ComboboxLabel (line 172) | function ComboboxLabel({
function ComboboxCollection (line 185) | function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.P...
function ComboboxEmpty (line 191) | function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty....
function ComboboxSeparator (line 204) | function ComboboxSeparator({
function ComboboxChips (line 217) | function ComboboxChips({
function ComboboxChip (line 234) | function ComboboxChip({
function ComboboxChipsInput (line 265) | function ComboboxChipsInput({
function useComboboxAnchor (line 278) | function useComboboxAnchor() {
FILE: src/components/ui/command.tsx
function Command (line 20) | function Command({
function CommandDialog (line 36) | function CommandDialog({
function CommandInput (line 68) | function CommandInput({
function CommandList (line 91) | function CommandList({
function CommandEmpty (line 107) | function CommandEmpty({
function CommandGroup (line 120) | function CommandGroup({
function CommandSeparator (line 136) | function CommandSeparator({
function CommandItem (line 149) | function CommandItem({
function CommandShortcut (line 169) | function CommandShortcut({
FILE: src/components/ui/context-menu.tsx
function ContextMenu (line 9) | function ContextMenu({
function ContextMenuTrigger (line 15) | function ContextMenuTrigger({
function ContextMenuGroup (line 28) | function ContextMenuGroup({
function ContextMenuPortal (line 36) | function ContextMenuPortal({
function ContextMenuSub (line 44) | function ContextMenuSub({
function ContextMenuRadioGroup (line 50) | function ContextMenuRadioGroup({
function ContextMenuContent (line 61) | function ContextMenuContent({
function ContextMenuItem (line 78) | function ContextMenuItem({
function ContextMenuSubTrigger (line 101) | function ContextMenuSubTrigger({
function ContextMenuSubContent (line 125) | function ContextMenuSubContent({
function ContextMenuCheckboxItem (line 138) | function ContextMenuCheckboxItem({
function ContextMenuRadioItem (line 169) | function ContextMenuRadioItem({
function ContextMenuLabel (line 198) | function ContextMenuLabel({
function ContextMenuSeparator (line 218) | function ContextMenuSeparator({
function ContextMenuShortcut (line 231) | function ContextMenuShortcut({
FILE: src/components/ui/dialog.tsx
function Dialog (line 10) | function Dialog({
function DialogTrigger (line 16) | function DialogTrigger({
function DialogPortal (line 22) | function DialogPortal({
function DialogClose (line 28) | function DialogClose({
function DialogOverlay (line 34) | function DialogOverlay({
function DialogContent (line 50) | function DialogContent({
function DialogHeader (line 88) | function DialogHeader({ className, ...props }: React.ComponentProps<"div...
function DialogFooter (line 98) | function DialogFooter({
function DialogTitle (line 125) | function DialogTitle({
function DialogDescription (line 138) | function DialogDescription({
FILE: src/components/ui/direction.tsx
function DirectionProvider (line 6) | function DirectionProvider({
FILE: src/components/ui/drawer.tsx
function Drawer (line 8) | function Drawer({
function DrawerTrigger (line 14) | function DrawerTrigger({
function DrawerPortal (line 20) | function DrawerPortal({
function DrawerClose (line 26) | function DrawerClose({
function DrawerOverlay (line 32) | function DrawerOverlay({
function DrawerContent (line 48) | function DrawerContent({
function DrawerHeader (line 71) | function DrawerHeader({ className, ...props }: React.ComponentProps<"div...
function DrawerFooter (line 84) | function DrawerFooter({ className, ...props }: React.ComponentProps<"div...
function DrawerTitle (line 94) | function DrawerTitle({
function DrawerDescription (line 107) | function DrawerDescription({
FILE: src/components/ui/dropdown-menu.tsx
function DropdownMenu (line 9) | function DropdownMenu({
function DropdownMenuPortal (line 15) | function DropdownMenuPortal({
function DropdownMenuTrigger (line 23) | function DropdownMenuTrigger({
function DropdownMenuContent (line 34) | function DropdownMenuContent({
function DropdownMenuGroup (line 53) | function DropdownMenuGroup({
function DropdownMenuItem (line 61) | function DropdownMenuItem({
function DropdownMenuCheckboxItem (line 84) | function DropdownMenuCheckboxItem({
function DropdownMenuRadioGroup (line 118) | function DropdownMenuRadioGroup({
function DropdownMenuRadioItem (line 129) | function DropdownMenuRadioItem({
function DropdownMenuLabel (line 161) | function DropdownMenuLabel({
function DropdownMenuSeparator (line 181) | function DropdownMenuSeparator({
function DropdownMenuShortcut (line 194) | function DropdownMenuShortcut({
function DropdownMenuSub (line 210) | function DropdownMenuSub({
function DropdownMenuSubTrigger (line 216) | function DropdownMenuSubTrigger({
function DropdownMenuSubContent (line 240) | function DropdownMenuSubContent({
FILE: src/components/ui/empty.tsx
function Empty (line 5) | function Empty({ className, ...props }: React.ComponentProps<"div">) {
function EmptyHeader (line 18) | function EmptyHeader({ className, ...props }: React.ComponentProps<"div"...
function EmptyMedia (line 43) | function EmptyMedia({
function EmptyTitle (line 58) | function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
function EmptyDescription (line 68) | function EmptyDescription({ className, ...props }: React.ComponentProps<...
function EmptyContent (line 81) | function EmptyContent({ className, ...props }: React.ComponentProps<"div...
FILE: src/components/ui/field.tsx
function FieldSet (line 10) | function FieldSet({ className, ...props }: React.ComponentProps<"fieldse...
function FieldLegend (line 23) | function FieldLegend({
function FieldGroup (line 41) | function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
function Field (line 72) | function Field({
function FieldContent (line 88) | function FieldContent({ className, ...props }: React.ComponentProps<"div...
function FieldLabel (line 101) | function FieldLabel({
function FieldTitle (line 118) | function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
function FieldDescription (line 131) | function FieldDescription({ className, ...props }: React.ComponentProps<...
function FieldSeparator (line 146) | function FieldSeparator({
function FieldError (line 176) | function FieldError({
FILE: src/components/ui/hover-card.tsx
function HoverCard (line 8) | function HoverCard({
function HoverCardTrigger (line 14) | function HoverCardTrigger({
function HoverCardContent (line 22) | function HoverCardContent({
FILE: src/components/ui/input-group.tsx
function InputGroup (line 11) | function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
function InputGroupAddon (line 46) | function InputGroupAddon({
function InputGroupButton (line 85) | function InputGroupButton({
function InputGroupText (line 104) | function InputGroupText({ className, ...props }: React.ComponentProps<"s...
function InputGroupInput (line 116) | function InputGroupInput({
function InputGroupTextarea (line 132) | function InputGroupTextarea({
FILE: src/components/ui/input-otp.tsx
function InputOTP (line 9) | function InputOTP({
function InputOTPGroup (line 30) | function InputOTPGroup({ className, ...props }: React.ComponentProps<"di...
function InputOTPSlot (line 43) | function InputOTPSlot({
function InputOTPSeparator (line 73) | function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
FILE: src/components/ui/input.tsx
function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<"inpu...
FILE: src/components/ui/item.tsx
function ItemGroup (line 8) | function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
function ItemSeparator (line 22) | function ItemSeparator({
function Item (line 58) | function Item({
function ItemMedia (line 95) | function ItemMedia({
function ItemContent (line 110) | function ItemContent({ className, ...props }: React.ComponentProps<"div"...
function ItemTitle (line 123) | function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
function ItemDescription (line 136) | function ItemDescription({ className, ...props }: React.ComponentProps<"...
function ItemActions (line 149) | function ItemActions({ className, ...props }: React.ComponentProps<"div"...
function ItemHeader (line 159) | function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
function ItemFooter (line 172) | function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
FILE: src/components/ui/kbd.tsx
function Kbd (line 3) | function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
function KbdGroup (line 16) | function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
FILE: src/components/ui/label.tsx
function Label (line 8) | function Label({
FILE: src/components/ui/marquee.tsx
constant ROOT_NAME (line 12) | const ROOT_NAME = "Marquee";
constant CONTENT_NAME (line 13) | const CONTENT_NAME = "MarqueeContent";
type Side (line 15) | type Side = "left" | "right" | "top" | "bottom";
type Orientation (line 16) | type Orientation = "horizontal" | "vertical";
type Direction (line 17) | type Direction = "ltr" | "rtl";
type RootElement (line 19) | type RootElement = React.ComponentRef<typeof Marquee>;
type ContentElement (line 20) | type ContentElement = React.ComponentRef<typeof MarqueeContent>;
type Dimensions (line 22) | interface Dimensions {
type ElementDimensions (line 27) | interface ElementDimensions {
function createResizeObserverStore (line 32) | function createResizeObserverStore() {
function useResizeObserverStore (line 216) | function useResizeObserverStore(
type DivProps (line 239) | interface DivProps extends React.ComponentProps<"div"> {
type MarqueeContextValue (line 243) | interface MarqueeContextValue {
function useMarqueeContext (line 260) | function useMarqueeContext(consumerName: string) {
type MarqueeProps (line 268) | interface MarqueeProps extends DivProps {
function Marquee (line 281) | function Marquee(props: MarqueeProps) {
function MarqueeContent (line 463) | function MarqueeContent(props: DivProps) {
function MarqueeItem (line 588) | function MarqueeItem(props: DivProps) {
type MarqueeEdgeProps (line 655) | interface MarqueeEdgeProps
function MarqueeEdge (line 659) | function MarqueeEdge(props: MarqueeEdgeProps) {
FILE: src/components/ui/menubar.tsx
function Menubar (line 9) | function Menubar({
function MenubarMenu (line 25) | function MenubarMenu({
function MenubarGroup (line 31) | function MenubarGroup({
function MenubarPortal (line 37) | function MenubarPortal({
function MenubarRadioGroup (line 43) | function MenubarRadioGroup({
function MenubarTrigger (line 51) | function MenubarTrigger({
function MenubarContent (line 67) | function MenubarContent({
function MenubarItem (line 88) | function MenubarItem({
function MenubarCheckboxItem (line 111) | function MenubarCheckboxItem({
function MenubarRadioItem (line 142) | function MenubarRadioItem({
function MenubarLabel (line 171) | function MenubarLabel({
function MenubarSeparator (line 188) | function MenubarSeparator({
function MenubarShortcut (line 201) | function MenubarShortcut({
function MenubarSub (line 217) | function MenubarSub({
function MenubarSubTrigger (line 223) | function MenubarSubTrigger({
function MenubarSubContent (line 247) | function MenubarSubContent({
FILE: src/components/ui/multi-select.tsx
type MultiSelectProps (line 44) | interface MultiSelectProps
FILE: src/components/ui/native-select.tsx
type NativeSelectProps (line 6) | type NativeSelectProps = Omit<React.ComponentProps<"select">, "size"> & {
function NativeSelect (line 10) | function NativeSelect({
function NativeSelectOption (line 35) | function NativeSelectOption({ ...props }: React.ComponentProps<"option">) {
function NativeSelectOptGroup (line 39) | function NativeSelectOptGroup({
FILE: src/components/ui/navigation-menu.tsx
function NavigationMenu (line 8) | function NavigationMenu({
function NavigationMenuList (line 32) | function NavigationMenuList({
function NavigationMenuItem (line 48) | function NavigationMenuItem({
function NavigationMenuTrigger (line 65) | function NavigationMenuTrigger({
function NavigationMenuContent (line 82) | function NavigationMenuContent({
function NavigationMenuViewport (line 98) | function NavigationMenuViewport({
function NavigationMenuLink (line 120) | function NavigationMenuLink({
function NavigationMenuIndicator (line 136) | function NavigationMenuIndicator({
FILE: src/components/ui/pagination.tsx
function Pagination (line 7) | function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
function PaginationContent (line 19) | function PaginationContent({
function PaginationItem (line 32) | function PaginationItem({ ...props }: React.ComponentProps<"li">) {
type PaginationLinkProps (line 36) | type PaginationLinkProps = {
function PaginationLink (line 41) | function PaginationLink({
function PaginationPrevious (line 64) | function PaginationPrevious({
function PaginationNext (line 82) | function PaginationNext({
function PaginationEllipsis (line 100) | function PaginationEllipsis({
FILE: src/components/ui/popover.tsx
function Popover (line 8) | function Popover({
function PopoverTrigger (line 14) | function PopoverTrigger({
function PopoverContent (line 20) | function PopoverContent({
function PopoverAnchor (line 42) | function PopoverAnchor({
function PopoverHeader (line 48) | function PopoverHeader({ className, ...props }: React.ComponentProps<"di...
function PopoverTitle (line 58) | function PopoverTitle({ className, ...props }: React.ComponentProps<"h2"...
function PopoverDescription (line 68) | function PopoverDescription({
FILE: src/components/ui/progress.tsx
function Progress (line 8) | function Progress({
FILE: src/components/ui/radio-group.tsx
function RadioGroup (line 8) | function RadioGroup({
function RadioGroupItem (line 21) | function RadioGroupItem({
FILE: src/components/ui/resizable.tsx
function ResizablePanelGroup (line 7) | function ResizablePanelGroup({
function ResizablePanel (line 23) | function ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {
function ResizableHandle (line 27) | function ResizableHandle({
FILE: src/components/ui/scroll-area.tsx
function ScrollArea (line 8) | function ScrollArea({
function ScrollBar (line 31) | function ScrollBar({
FILE: src/components/ui/select.tsx
function Select (line 9) | function Select({
function SelectGroup (line 15) | function SelectGroup({
function SelectValue (line 28) | function SelectValue({
function SelectTrigger (line 34) | function SelectTrigger({
function SelectContent (line 60) | function SelectContent({
function SelectLabel (line 93) | function SelectLabel({
function SelectItem (line 106) | function SelectItem({
function SelectSeparator (line 130) | function SelectSeparator({
function SelectScrollUpButton (line 143) | function SelectScrollUpButton({
function SelectScrollDownButton (line 162) | function SelectScrollDownButton({
FILE: src/components/ui/separator.tsx
function Separator (line 8) | function Separator({
FILE: src/components/ui/sheet.tsx
function Sheet (line 10) | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive....
function SheetTrigger (line 14) | function SheetTrigger({
function SheetClose (line 20) | function SheetClose({
function SheetPortal (line 26) | function SheetPortal({
function SheetOverlay (line 32) | function SheetOverlay({
function SheetContent (line 48) | function SheetContent({
function SheetHeader (line 89) | function SheetHeader({ className, ...props }: React.ComponentProps<"div"...
function SheetFooter (line 99) | function SheetFooter({ className, ...props }: React.ComponentProps<"div"...
function SheetTitle (line 109) | function SheetTitle({
function SheetDescription (line 122) | function SheetDescription({
FILE: src/components/ui/sidebar.tsx
constant SIDEBAR_COOKIE_NAME (line 27) | const SIDEBAR_COOKIE_NAME = "sidebar_state"
constant SIDEBAR_COOKIE_MAX_AGE (line 28) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
constant SIDEBAR_WIDTH (line 29) | const SIDEBAR_WIDTH = "16rem"
constant SIDEBAR_WIDTH_MOBILE (line 30) | const SIDEBAR_WIDTH_MOBILE = "18rem"
constant SIDEBAR_WIDTH_ICON (line 31) | const SIDEBAR_WIDTH_ICON = "3rem"
constant SIDEBAR_KEYBOARD_SHORTCUT (line 32) | const SIDEBAR_KEYBOARD_SHORTCUT = "b"
type SidebarContextProps (line 34) | type SidebarContextProps = {
function useSidebar (line 46) | function useSidebar() {
function SidebarProvider (line 55) | function SidebarProvider({
function Sidebar (line 151) | function Sidebar({
function SidebarTrigger (line 253) | function SidebarTrigger({
function SidebarRail (line 279) | function SidebarRail({ className, ...props }: React.ComponentProps<"butt...
function SidebarInset (line 304) | function SidebarInset({ className, ...props }: React.ComponentProps<"mai...
function SidebarInput (line 317) | function SidebarInput({
function SidebarHeader (line 331) | function SidebarHeader({ className, ...props }: React.ComponentProps<"di...
function SidebarFooter (line 342) | function SidebarFooter({ className, ...props }: React.ComponentProps<"di...
function SidebarSeparator (line 353) | function SidebarSeparator({
function SidebarContent (line 367) | function SidebarContent({ className, ...props }: React.ComponentProps<"d...
function SidebarGroup (line 381) | function SidebarGroup({ className, ...props }: React.ComponentProps<"div...
function SidebarGroupLabel (line 392) | function SidebarGroupLabel({
function SidebarGroupAction (line 412) | function SidebarGroupAction({
function SidebarGroupContent (line 432) | function SidebarGroupContent({
function SidebarMenu (line 446) | function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
function SidebarMenuItem (line 457) | function SidebarMenuItem({ className, ...props }: React.ComponentProps<"...
function SidebarMenuButton (line 490) | function SidebarMenuButton({
function SidebarMenuAction (line 540) | function SidebarMenuAction({
function SidebarMenuBadge (line 566) | function SidebarMenuBadge({
function SidebarMenuSkeleton (line 583) | function SidebarMenuSkeleton({
function SidebarMenuSub (line 621) | function SidebarMenuSub({ className, ...props }: React.ComponentProps<"u...
function SidebarMenuSubItem (line 635) | function SidebarMenuSubItem({
function SidebarMenuSubButton (line 649) | function SidebarMenuSubButton({
FILE: src/components/ui/skeleton.tsx
function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
FILE: src/components/ui/slider.tsx
function Slider (line 8) | function Slider({
FILE: src/components/ui/spinner.tsx
function Spinner (line 4) | function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
FILE: src/components/ui/switch.tsx
function Switch (line 8) | function Switch({
FILE: src/components/ui/table.tsx
function Table (line 7) | function Table({ className, ...props }: React.ComponentProps<"table">) {
function TableHeader (line 22) | function TableHeader({ className, ...props }: React.ComponentProps<"thea...
function TableBody (line 32) | function TableBody({ className, ...props }: React.ComponentProps<"tbody"...
function TableFooter (line 42) | function TableFooter({ className, ...props }: React.ComponentProps<"tfoo...
function TableRow (line 55) | function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
function TableHead (line 68) | function TableHead({ className, ...props }: React.ComponentProps<"th">) {
function TableCell (line 81) | function TableCell({ className, ...props }: React.ComponentProps<"td">) {
function TableCaption (line 94) | function TableCaption({
FILE: src/components/ui/tabs.tsx
function Tabs (line 9) | function Tabs({
function TabsList (line 42) | function TabsList({
function TabsTrigger (line 58) | function TabsTrigger({
function TabsContent (line 77) | function TabsContent({
FILE: src/components/ui/textarea.tsx
function Textarea (line 5) | function Textarea({ className, ...props }: React.ComponentProps<"textare...
FILE: src/components/ui/toggle-group.tsx
function ToggleGroup (line 22) | function ToggleGroup({
function ToggleGroupItem (line 58) | function ToggleGroupItem({
FILE: src/components/ui/toggle.tsx
function Toggle (line 30) | function Toggle({
FILE: src/components/ui/tooltip.tsx
function TooltipProvider (line 8) | function TooltipProvider({
function Tooltip (line 21) | function Tooltip({
function TooltipTrigger (line 27) | function TooltipTrigger({
function TooltipContent (line 33) | function TooltipContent({
FILE: src/hooks/use-bookmark.ts
function useBookmarks (line 3) | function useBookmarks() {
FILE: src/hooks/use-categories.ts
type Category (line 7) | interface Category {
constant CATEGORY_DESCRIPTIONS (line 14) | const CATEGORY_DESCRIPTIONS: Record<string, string> = {
function useCategories (line 33) | function useCategories() {
FILE: src/hooks/use-debounce.ts
function useDebounce (line 3) | function useDebounce<T>(value: T, delay: number): T {
FILE: src/hooks/use-github-auth.ts
type GitHubAuthState (line 4) | interface GitHubAuthState {
type GitHubUser (line 15) | interface GitHubUser {
type DeviceFlowResponse (line 22) | interface DeviceFlowResponse {
function useGitHubAuth (line 30) | function useGitHubAuth() {
FILE: src/hooks/use-mobile.ts
constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
function useIsMobile (line 5) | function useIsMobile() {
FILE: src/hooks/use-pr-submission.ts
type SubmissionData (line 3) | interface SubmissionData {
type PRSubmissionResult (line 10) | interface PRSubmissionResult {
function usePRSubmission (line 17) | function usePRSubmission() {
FILE: src/hooks/use-readme.ts
constant EXCLUDED_CATEGORIES (line 7) | const EXCLUDED_CATEGORIES = ["Star History", "Contributors"];
type Resource (line 9) | interface Resource {
constant CACHE_DURATION (line 21) | const CACHE_DURATION = 30 * 60 * 1000;
function fetchAndParseReadme (line 23) | async function fetchAndParseReadme(): Promise<Resource[]> {
FILE: src/hooks/use-website-preview.ts
type WebsitePreviewProps (line 5) | interface WebsitePreviewProps {
function useWebsitePreview (line 10) | function useWebsitePreview({ url, name }: WebsitePreviewProps) {
FILE: src/lib/compose-refs.ts
type PossibleRef (line 3) | type PossibleRef<T> = React.Ref<T> | undefined;
function setRef (line 9) | function setRef<T>(ref: PossibleRef<T>, value: T) {
function composeRefs (line 23) | function composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
function useComposedRefs (line 57) | function useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallbac...
FILE: src/lib/config.ts
constant GITHUB_CONFIG (line 2) | const GITHUB_CONFIG = {
constant PR_TEMPLATE (line 30) | const PR_TEMPLATE = {
constant ERROR_MESSAGES (line 70) | const ERROR_MESSAGES = {
constant STATUS_MESSAGES (line 81) | const STATUS_MESSAGES = {
FILE: src/lib/slugs.ts
function categoryNameToSlug (line 5) | function categoryNameToSlug(categoryName: string): string {
function slugToCategoryName (line 14) | function slugToCategoryName(slug: string): string {
function createCategorySlugMap (line 34) | function createCategorySlugMap(
function createSlugToCategoryMap (line 44) | function createSlugToCategoryMap(
function titleToSlug (line 54) | function titleToSlug(title: string): string {
function slugToTitle (line 63) | function slugToTitle(slug: string): string {
FILE: src/lib/utils.ts
function cn (line 4) | function cn(...inputs: ClassValue[]) {
FILE: src/providers/providers.tsx
function Providers (line 6) | function Providers({ children }: { children: React.ReactNode }) {
FILE: src/providers/theme-provider.tsx
function ThemeProvider (line 8) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {
Condensed preview — 128 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (536K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 900,
"preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
},
{
"path": ".github/pull_request_template.md",
"chars": 2236,
"preview": "---\nname: \"feat: Add new awesome resource\"\nabout: \"Propose adding a new awesome resource related to shadcn/ui\"\nlabels:\n "
},
{
"path": ".github/workflows/add-dates.yml",
"chars": 765,
"preview": "name: Add Dates to New Resources\n\non:\n push:\n branches:\n - main\n paths:\n - 'README.md'\n\njobs:\n add-dat"
},
{
"path": ".github/workflows/format-readme.yml",
"chars": 852,
"preview": "name: Format README\n\non:\n pull_request:\n paths:\n - \"README.md\"\n workflow_dispatch:\n\njobs:\n format-readme:\n "
},
{
"path": ".gitignore",
"chars": 407,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "DEVELOPMENT.md",
"chars": 6284,
"preview": "# Development Guide\n\nThis guide covers the development setup, architecture, and configuration of the awesome-shadcn-ui w"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "MIT License\n\nCopyright (c) 2024 João Inácio Neto\n\nPermission is hereby granted, free of charge, to any person obtaining "
},
{
"path": "README.md",
"chars": 75927,
"preview": "<p align=\"center\">\n <a href=\"https://awesome-shadcn-ui.vercel.app\" target=\"_blank\">\n <img width=\"400\" src=\"public/logo"
},
{
"path": "components.json",
"chars": 571,
"preview": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"radix-lyra\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\":"
},
{
"path": "next.config.mjs",
"chars": 331,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n reactStrictMode: true,\n eslint: {\n ignoreDuringBuild"
},
{
"path": "open-next.config.ts",
"chars": 107,
"preview": "import { defineCloudflareConfig } from \"@opennextjs/cloudflare\";\n\nexport default defineCloudflareConfig();\n"
},
{
"path": "package.json",
"chars": 2295,
"preview": "{\n \"name\": \"awesome-shadcn-ui\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev --turbo\",\n"
},
{
"path": "postcss.config.mjs",
"chars": 146,
"preview": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n plugins: {\n \"@tailwindcss/postcss\": {},\n },\n}"
},
{
"path": "scripts/add-dates.js",
"chars": 3068,
"preview": "const fs = require('fs');\n\nconst path = 'README.md';\nlet content;\ntry {\n content = fs.readFileSync(path, 'utf8');\n} cat"
},
{
"path": "scripts/format-readme.js",
"chars": 1125,
"preview": "const fs = require('fs');\nconst path = 'README.md';\n\nlet content;\ntry {\n content = fs.readFileSync(path, 'utf8');\n} cat"
},
{
"path": "src/app/[...not-found]/page.tsx",
"chars": 106,
"preview": "import { notFound } from \"next/navigation\";\n\nexport default function CatchAllNotFound() {\n notFound();\n}\n"
},
{
"path": "src/app/api/github/callback/route.ts",
"chars": 962,
"preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nexport async function GET(request: NextRequest) {\n const sear"
},
{
"path": "src/app/api/github/device-flow/route.ts",
"chars": 2352,
"preview": "import { ERROR_MESSAGES, GITHUB_CONFIG } from \"@/lib/config\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\n"
},
{
"path": "src/app/api/submit-resource/route.ts",
"chars": 6134,
"preview": "import { NextRequest, NextResponse } from \"next/server\";\nimport { Octokit } from \"@octokit/rest\";\n\nconst GITHUB_CONFIG ="
},
{
"path": "src/app/bookmarks/error.tsx",
"chars": 2436,
"preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { AlertTriangle, ArrowLeft, Home, RefreshCw } fro"
},
{
"path": "src/app/bookmarks/page.tsx",
"chars": 10440,
"preview": "\"use client\";\n\nimport { ItemGrid } from \"@/components/item-grid\";\nimport { PageHeader } from \"@/components/layout/page-h"
},
{
"path": "src/app/categories/[category]/[id]/error.tsx",
"chars": 2411,
"preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { AlertTriangle, ArrowLeft, Home, RefreshCw } fro"
},
{
"path": "src/app/categories/[category]/[id]/page.tsx",
"chars": 17579,
"preview": "\"use client\";\n\nimport { PageHeader } from \"@/components/layout/page-header\";\nimport { Badge } from \"@/components/ui/badg"
},
{
"path": "src/app/categories/[category]/error.tsx",
"chars": 2431,
"preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { AlertTriangle, ArrowLeft, Home, RefreshCw } fro"
},
{
"path": "src/app/categories/[category]/page.tsx",
"chars": 9122,
"preview": "\"use client\";\n\nimport { ItemGrid } from \"@/components/item-grid\";\nimport { PageHeader } from \"@/components/layout/page-h"
},
{
"path": "src/app/categories/error.tsx",
"chars": 2188,
"preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { AlertTriangle, Home, RefreshCw } from \"lucide-r"
},
{
"path": "src/app/categories/page.tsx",
"chars": 6993,
"preview": "\"use client\";\n\nimport { PageHeader } from \"@/components/layout/page-header\";\nimport { Badge } from \"@/components/ui/badg"
},
{
"path": "src/app/error.tsx",
"chars": 2071,
"preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { AlertTriangle, RefreshCw } from \"lucide-react\";"
},
{
"path": "src/app/global-error.tsx",
"chars": 1770,
"preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { AlertOctagon } from \"lucide-react\";\nimport { us"
},
{
"path": "src/app/globals.css",
"chars": 9689,
"preview": "@import \"tailwindcss\";\n@import \"tw-animate-css\";\n@import \"shadcn/tailwind.css\";\n\n@custom-variant dark (&:is(.dark *));\n\n"
},
{
"path": "src/app/layout.tsx",
"chars": 3032,
"preview": "import { Footer } from \"@/components/layout/footer\";\nimport { Header } from \"@/components/layout/header\";\nimport { Provi"
},
{
"path": "src/app/not-found.tsx",
"chars": 2531,
"preview": "import { Button } from \"@/components/ui/button\";\nimport Link from \"next/link\";\n\nexport default function NotFound() {\n r"
},
{
"path": "src/app/page.tsx",
"chars": 2565,
"preview": "\"use client\";\n\nimport { SubmitCTA } from \"@/components/sections/cta-submit\";\nimport Hero from \"@/components/sections/her"
},
{
"path": "src/components/github-stars.tsx",
"chars": 646,
"preview": "\"use client\";\n\nimport { Octokit } from \"@octokit/rest\";\nimport { useEffect, useState } from \"react\";\n\nasync function get"
},
{
"path": "src/components/item-card.tsx",
"chars": 4226,
"preview": "import {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { ArrowRi"
},
{
"path": "src/components/item-grid.tsx",
"chars": 2965,
"preview": "import React from \"react\";\nimport { Resource } from \"@/hooks/use-readme\";\nimport { AnimatePresence, LayoutGroup, motion "
},
{
"path": "src/components/kibo-ui/theme-switcher/index.tsx",
"chars": 2253,
"preview": "\"use client\";\n\nimport { useTheme } from \"next-themes\";\nimport { Monitor, Moon, Sun } from \"lucide-react\";\nimport { motio"
},
{
"path": "src/components/layout/footer.tsx",
"chars": 6640,
"preview": "\"use client\";\n\nimport { Separator } from \"@/components/ui/separator\";\nimport { ThemeSwitcher } from \"@/components/kibo-u"
},
{
"path": "src/components/layout/header.tsx",
"chars": 7692,
"preview": "\"use client\";\n\nimport { PRSubmissionDialog } from \"@/components/pr-submission-dialog\";\nimport { ThemeSwitcher } from \"@/"
},
{
"path": "src/components/layout/page-header.tsx",
"chars": 2670,
"preview": "\"use client\";\n\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n Breadcru"
},
{
"path": "src/components/logo.tsx",
"chars": 525,
"preview": "import Image from \"next/image\";\n\ninterface LogoProps {\n className?: string;\n}\n\nexport function Logo({ className = \"h-5 "
},
{
"path": "src/components/pagination-controls.tsx",
"chars": 2850,
"preview": "import {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimpor"
},
{
"path": "src/components/pr-submission-dialog.tsx",
"chars": 8122,
"preview": "\"use client\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n"
},
{
"path": "src/components/search-filter-controls.tsx",
"chars": 2553,
"preview": "import Sort, { SortOption } from \"@/components/sort\";\nimport { Input } from \"@/components/ui/input\";\nimport { MultiSelec"
},
{
"path": "src/components/sections/cta-submit.tsx",
"chars": 3158,
"preview": "\"use client\";\n\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { motion, useInView } from \"motion/react"
},
{
"path": "src/components/sections/hero.tsx",
"chars": 2517,
"preview": "\"use client\";\n\nimport { HeroLogo } from \"@/components/logo\";\nimport { Sponsorship } from \"./sponsorship\";\nimport { motio"
},
{
"path": "src/components/sections/items-list.tsx",
"chars": 9231,
"preview": "\"use client\";\n\nimport { isValid, parseISO } from \"date-fns\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimp"
},
{
"path": "src/components/sections/sponsorship.tsx",
"chars": 2579,
"preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n Marquee,\n MarqueeContent,\n MarqueeEdge,\n M"
},
{
"path": "src/components/sort.tsx",
"chars": 2368,
"preview": "import {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimpor"
},
{
"path": "src/components/sponsor-card.tsx",
"chars": 2764,
"preview": "import {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { ArrowRi"
},
{
"path": "src/components/sponsors/shadcn-blocks-logo.tsx",
"chars": 1530,
"preview": "import * as React from \"react\";\n\ninterface ShadcnBlocksLogoProps {\n className?: string;\n}\n\nfunction ShadcnBlocksLogo({ "
},
{
"path": "src/components/sponsors/shadcn-studio-logo.tsx",
"chars": 1548,
"preview": "import * as React from \"react\";\n\ninterface ShadcnStudioLogoProps {\n className?: string;\n}\n\nfunction ShadcnStudioLogo({ "
},
{
"path": "src/components/sponsors/shadcn-ui-kit-logo.tsx",
"chars": 402,
"preview": "import * as React from \"react\";\nimport Image from \"next/image\";\n\ninterface ShadcnUiKitLogoProps {\n className?: string;\n"
},
{
"path": "src/components/sponsors/sponsors.tsx",
"chars": 1142,
"preview": "import { ReactNode } from \"react\";\nimport ShadcnStudioLogo from \"./shadcn-studio-logo\";\nimport ShadcnBlocksLogo from \"./"
},
{
"path": "src/components/theme-toggle.tsx",
"chars": 1269,
"preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Moon, Sun } from \"lucide-react\";\nimport { useTheme } from \"next-"
},
{
"path": "src/components/ui/accordion.tsx",
"chars": 2687,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Accordion as AccordionPrimitive } from \"radix-ui\"\n\nimport { cn } f"
},
{
"path": "src/components/ui/alert-dialog.tsx",
"chars": 5564,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { AlertDialog as AlertDialogPrimitive } from \"radix-ui\"\n\nimport { cn"
},
{
"path": "src/components/ui/alert.tsx",
"chars": 2124,
"preview": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/"
},
{
"path": "src/components/ui/aspect-ratio.tsx",
"chars": 274,
"preview": "\"use client\"\n\nimport { AspectRatio as AspectRatioPrimitive } from \"radix-ui\"\n\nfunction AspectRatio({\n ...props\n}: React"
},
{
"path": "src/components/ui/avatar.tsx",
"chars": 3098,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Avatar as AvatarPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@"
},
{
"path": "src/components/ui/badge.tsx",
"chars": 1866,
"preview": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"r"
},
{
"path": "src/components/ui/breadcrumb.tsx",
"chars": 2476,
"preview": "import * as React from \"react\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChevronRightIc"
},
{
"path": "src/components/ui/button-group.tsx",
"chars": 2247,
"preview": "import { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@"
},
{
"path": "src/components/ui/button.tsx",
"chars": 3016,
"preview": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"r"
},
{
"path": "src/components/ui/calendar.tsx",
"chars": 8212,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n DayPicker,\n getDefaultClassNames,\n type DayButton,\n type Loca"
},
{
"path": "src/components/ui/card.tsx",
"chars": 2619,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Card({\n className,\n size = \"default\",\n ..."
},
{
"path": "src/components/ui/carousel.tsx",
"chars": 5599,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport useEmblaCarousel, {\n type UseEmblaCarouselType,\n} from \"embla-carou"
},
{
"path": "src/components/ui/chart.tsx",
"chars": 10018,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as RechartsPrimitive from \"recharts\"\n\nimport { cn } from \"@/lib/ut"
},
{
"path": "src/components/ui/checkbox.tsx",
"chars": 1399,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Checkbox as CheckboxPrimitive } from \"radix-ui\"\n\nimport { cn } fro"
},
{
"path": "src/components/ui/collapsible.tsx",
"chars": 795,
"preview": "\"use client\"\n\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\"\n\nfunction Collapsible({\n ...props\n}: React"
},
{
"path": "src/components/ui/combobox.tsx",
"chars": 9066,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Combobox as ComboboxPrimitive } from \"@base-ui/react\"\n\nimport { cn"
},
{
"path": "src/components/ui/command.tsx",
"chars": 4921,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Command as CommandPrimitive } from \"cmdk\"\n\nimport { cn } from \"@/l"
},
{
"path": "src/components/ui/context-menu.tsx",
"chars": 8270,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { ContextMenu as ContextMenuPrimitive } from \"radix-ui\"\n\nimport { cn"
},
{
"path": "src/components/ui/dialog.tsx",
"chars": 4139,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as DialogPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@"
},
{
"path": "src/components/ui/direction.tsx",
"chars": 506,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Direction } from \"radix-ui\"\n\nfunction DirectionProvider({\n dir,\n "
},
{
"path": "src/components/ui/drawer.tsx",
"chars": 4338,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Drawer as DrawerPrimitive } from \"vaul\"\n\nimport { cn } from \"@/lib"
},
{
"path": "src/components/ui/dropdown-menu.tsx",
"chars": 8800,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\"\n\nimport { "
},
{
"path": "src/components/ui/empty.tsx",
"chars": 2349,
"preview": "import { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Empty({ cl"
},
{
"path": "src/components/ui/field.tsx",
"chars": 6032,
"preview": "\"use client\"\n\nimport { useMemo } from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport "
},
{
"path": "src/components/ui/hover-card.tsx",
"chars": 1530,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { HoverCard as HoverCardPrimitive } from \"radix-ui\"\n\nimport { cn } f"
},
{
"path": "src/components/ui/input-group.tsx",
"chars": 5135,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport {"
},
{
"path": "src/components/ui/input-otp.tsx",
"chars": 2549,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { OTPInput, OTPInputContext } from \"input-otp\"\n\nimport { cn } from \""
},
{
"path": "src/components/ui/input.tsx",
"chars": 968,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Input({ className, type, ...props }: React.Co"
},
{
"path": "src/components/ui/item.tsx",
"chars": 4772,
"preview": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"r"
},
{
"path": "src/components/ui/kbd.tsx",
"chars": 839,
"preview": "import { cn } from \"@/lib/utils\"\n\nfunction Kbd({ className, ...props }: React.ComponentProps<\"kbd\">) {\n return (\n <k"
},
{
"path": "src/components/ui/label.tsx",
"chars": 594,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Label as LabelPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/l"
},
{
"path": "src/components/ui/marquee.tsx",
"chars": 17648,
"preview": "\"use client\";\r\n\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\nimport {\r\n Direction as DirectionP"
},
{
"path": "src/components/ui/menubar.tsx",
"chars": 8425,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Menubar as MenubarPrimitive } from \"radix-ui\"\n\nimport { cn } from "
},
{
"path": "src/components/ui/multi-select.tsx",
"chars": 11973,
"preview": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport { CheckIcon, ChevronDown, XCircle, XIcon } fro"
},
{
"path": "src/components/ui/native-select.tsx",
"chars": 1932,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\ntype Na"
},
{
"path": "src/components/ui/navigation-menu.tsx",
"chars": 6547,
"preview": "import * as React from \"react\"\nimport { cva } from \"class-variance-authority\"\nimport { NavigationMenu as NavigationMenuP"
},
{
"path": "src/components/ui/pagination.tsx",
"chars": 2829,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport "
},
{
"path": "src/components/ui/popover.tsx",
"chars": 2399,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\n\nimport { cn } from "
},
{
"path": "src/components/ui/progress.tsx",
"chars": 746,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Progress as ProgressPrimitive } from \"radix-ui\"\n\nimport { cn } fro"
},
{
"path": "src/components/ui/radio-group.tsx",
"chars": 1692,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\"\n\nimport { cn }"
},
{
"path": "src/components/ui/resizable.tsx",
"chars": 1673,
"preview": "\"use client\"\n\nimport * as ResizablePrimitive from \"react-resizable-panels\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction R"
},
{
"path": "src/components/ui/scroll-area.tsx",
"chars": 1692,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { ScrollArea as ScrollAreaPrimitive } from \"radix-ui\"\n\nimport { cn }"
},
{
"path": "src/components/ui/select.tsx",
"chars": 6646,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Select as SelectPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@"
},
{
"path": "src/components/ui/separator.tsx",
"chars": 644,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Separator as SeparatorPrimitive } from \"radix-ui\"\n\nimport { cn } f"
},
{
"path": "src/components/ui/sheet.tsx",
"chars": 4594,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as SheetPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/"
},
{
"path": "src/components/ui/sidebar.tsx",
"chars": 21245,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { "
},
{
"path": "src/components/ui/skeleton.tsx",
"chars": 277,
"preview": "import { cn } from \"@/lib/utils\"\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n "
},
{
"path": "src/components/ui/slider.tsx",
"chars": 1853,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Slider as SliderPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@"
},
{
"path": "src/components/ui/sonner.tsx",
"chars": 1226,
"preview": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, type ToasterProps } from \"sonner\"\nimpor"
},
{
"path": "src/components/ui/spinner.tsx",
"chars": 302,
"preview": "import { cn } from \"@/lib/utils\"\nimport { Loader2Icon } from \"lucide-react\"\n\nfunction Spinner({ className, ...props }: R"
},
{
"path": "src/components/ui/switch.tsx",
"chars": 1748,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Switch as SwitchPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@"
},
{
"path": "src/components/ui/table.tsx",
"chars": 2372,
"preview": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Table({ className, ...props }: "
},
{
"path": "src/components/ui/tabs.tsx",
"chars": 3449,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { "
},
{
"path": "src/components/ui/textarea.tsx",
"chars": 842,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Textarea({ className, ...props }: React.Compo"
},
{
"path": "src/components/ui/toggle-group.tsx",
"chars": 3027,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { Toggl"
},
{
"path": "src/components/ui/toggle.tsx",
"chars": 1526,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { "
},
{
"path": "src/components/ui/tooltip.tsx",
"chars": 1943,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\"\n\nimport { cn } from "
},
{
"path": "src/hooks/use-bookmark.ts",
"chars": 2256,
"preview": "import { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport function useBookmarks() {\n const [bookmarkedI"
},
{
"path": "src/hooks/use-categories.ts",
"chars": 2814,
"preview": "\"use client\";\n\nimport { categoryNameToSlug } from \"@/lib/slugs\";\nimport { useEffect, useState } from \"react\";\nimport { f"
},
{
"path": "src/hooks/use-debounce.ts",
"chars": 386,
"preview": "import { useEffect, useState } from \"react\";\n\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debo"
},
{
"path": "src/hooks/use-github-auth.ts",
"chars": 5924,
"preview": "import { Octokit } from \"@octokit/rest\";\nimport { useCallback, useRef, useState } from \"react\";\n\nexport interface GitHub"
},
{
"path": "src/hooks/use-mobile.ts",
"chars": 565,
"preview": "import * as React from \"react\"\n\nconst MOBILE_BREAKPOINT = 768\n\nexport function useIsMobile() {\n const [isMobile, setIsM"
},
{
"path": "src/hooks/use-pr-submission.ts",
"chars": 2378,
"preview": "import { useCallback, useState } from \"react\";\n\nexport interface SubmissionData {\n name: string;\n description: string;"
},
{
"path": "src/hooks/use-readme.ts",
"chars": 3880,
"preview": "import { GITHUB_CONFIG } from \"@/lib/config\";\nimport { titleToSlug } from \"@/lib/slugs\";\nimport { Octokit } from \"@octok"
},
{
"path": "src/hooks/use-website-preview.ts",
"chars": 2147,
"preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\ninterface WebsitePreviewProps {\n url: string;\n name: stri"
},
{
"path": "src/lib/compose-refs.ts",
"chars": 1857,
"preview": "import * as React from \"react\";\r\n\r\ntype PossibleRef<T> = React.Ref<T> | undefined;\r\n\r\n/**\r\n * Set a given ref to a given"
},
{
"path": "src/lib/config.ts",
"chars": 2869,
"preview": "// GitHub OAuth App Configuration\nexport const GITHUB_CONFIG = {\n // OAuth App Client ID - https://github.com/marketpla"
},
{
"path": "src/lib/slugs.ts",
"chars": 2178,
"preview": "/**\n * Utility functions for converting between category names and URL-friendly slugs\n */\n\nexport function categoryNameT"
},
{
"path": "src/lib/utils.ts",
"chars": 166,
"preview": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: Cla"
},
{
"path": "src/providers/providers.tsx",
"chars": 375,
"preview": "\"use client\";\n\nimport { Toaster } from \"sonner\";\nimport { ThemeProvider } from \"./theme-provider\";\n\nexport function Prov"
},
{
"path": "src/providers/theme-provider.tsx",
"chars": 264,
"preview": "\"use client\";\n\nimport {\n ThemeProvider as NextThemesProvider,\n type ThemeProviderProps,\n} from \"next-themes\";\n\nexport "
},
{
"path": "tsconfig.json",
"chars": 699,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\n \"dom\",\n \"dom.iterable\",\n \"esnext\"\n ],\n "
},
{
"path": "wrangler.jsonc",
"chars": 395,
"preview": "{\n \"$schema\": \"./node_modules/wrangler/config-schema.json\",\n \"main\": \".open-next/worker.js\",\n \"name\": \"awesome-shadcn"
}
]
About this extraction
This page contains the full source code of the birobirobiro/awesome-shadcn-ui GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 128 files (493.9 KB), approximately 132.2k tokens, and a symbol index with 448 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.