Note
This is a fork of the original Origin UI project. This project is not affiliated with the original. I'm grateful for their work and have created these Svelte components copied from their design.
Note
This is a work in progress. For some components the necessary libraries are coming soon (e.g. Bits UI) or are just not available yet (e.g. React Payment Inputs). Maybe i will add them in the future myself.
Origin UI - Svelte is a collection of copy-and-paste components for quickly building app UIs using Svelte.
- Features
- Demo
- Acknowledgements
- Differences from the original
- Getting Started
- Usage
- Contributing
- Need Help?
- Terms of Use
- Contact
- Notes
- Origin UI - The original project that this Svelte version is copied from
- Svelte
- TailwindCSS
- Lucide Icons
- Bits UI
The Original Origin UI is built with Next.js. This is a built with Svelte.
- Svelte instead of
Next.js - Lucide Svelte instead of
Lucide React - Bits UI instead of
Radix UI
If you want to use the components in your project, you need to setup the following:
- Svelte
- TailwindCSS
- Bits UI (Next)
- or other dependencies (see src/lib/constants.ts)
Note
This project uses pnpm as package manager.
-
Setup
git clone https://github.com/max-got/originui-svelte.git cd originui-svelte pnpm install
-
Development
pnpm dev
- Components are previewed at
http://localhost:5173
- Components are previewed at
-
Code Quality
pnpm lint # Run ESLint pnpm format # Run Prettier
You can copy and use the components in your Svelte project. Note that some components may require additional libraries - refer to the listed dependencies in the component preview.
In the src/lib/utils.ts
folder you will find the common cn
function for tailwindcss class merging.
In the src/lib/hooks
folder you will find the common hooks.
In the src/lib/actions
folder you will find the common actions.
You need to copy the base components from the src/lib/components/ui
folder to your project and adjust the imports accordingly.
Import the CSS in your src/lib/app.css
file (the following is based on tailwindcss):
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 100%;
--border: 240 5.9% 90%;
--input: 240 4.9% 83.9%;
--ring: 240 5% 64.9%;
--radius: 0.5rem;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 5.9% 10%;
--muted-foreground: 240 4.4% 58%;
--accent: 240 5.9% 10%;
--accent-foreground: 0 0% 98%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 100%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 3.8% 46.1%;
}
}
We welcome contributions to Origin UI - Svelte!
This guide will help you understand our project structure and contribution workflow.
src/
├── lib/
│ ├── components/ # UI Components organized by type
│ │ ├── ui/ # Base UI components
│ │ │ ├── badge.svelte # one component
│ │ │ ├── button.svelte # one component
│ │ │ ├── accordion.svelte # needs multiple components
│ │ │ │ │── accordion-item.svelte # Base component Accordion Item
│ │ │ │ │── accordion-trigger.svelte # Base component Accordion Trigger
│ │ │ │ │── ...
│ │ │ └── ...
│ │ ├── inputs/ # Input components
│ │ │ ├── input-01.svelte
│ │ │ ├── input-02.svelte
│ │ │ └── ...
│ │ ├── buttons/ # Button components
│ │ │ ├── button-01.svelte
│ │ │ ├── button-02.svelte
│ │ │ └── ...
│ │ └─ ... # Other component categories
│ ├── utils/ # Utility functions
│ ├── types/ # TypeScript type definitions
│ ├── hooks/ # Svelte hooks
│ ├── actions/ # Svelte actions
│ └── config/ # Configuration files
The project uses an automated component registry system that connects your components to the routing system. Here's how it works:
-
Registry Generation
pnpm generate:registry
This script automatically:
- Scans the
src/lib/components/
directory - Generates
src/lib/componentRegistry.types.ts
with component mappings - Updates component type definitions and directory structures
- Scans the
-
Registry Architecture
src/ ├── lib/ │ ├── componentRegistry.types.ts # Auto-generated types │ ├── componentRegistry.ts # Registry implementation │ └── data/api/ │ ├── components.ts # API endpoints │ ├── components.handler.ts # Request handlers │ └── components.route.ts # Route definitions ├── routes/ │ └── (components)/ │ └── [path=componentsPath]/ │ ├── +page.server.ts # Dynamic route handling │ └── [directory]/[id]/ │ └── +page.server.ts # Component page handling └── params/ ├── componentDirectory.ts # Directory parameter validation ├── componentId.ts # Component ID validation └── componentsPath.ts # Path parameter validation
-
How It Works
- The registry system creates a type-safe mapping between:
- Component files in your directories
- URL routes and parameters
- API endpoints
- SvelteKit param matchers use these types to validate URLs
- The system enables dynamic routing while maintaining type safety
- The registry system creates a type-safe mapping between:
-
When to Run Registry Generation Run
pnpm generate:registry
when you:- Add new component files
- Create new component categories
- Rename components or directories
- Update component structure
Important
Always run pnpm generate:registry
after making changes to component files or structure. This ensures the routing system stays in sync with your components.
The project includes an automated dependency detection system that analyzes component source code and manages dependencies.
-
Dependency Configuration
Dependencies must be registered in
src/lib/constants.ts
:export const POSSIBLE_DEPENDENCIES = [ { dev: false, // Whether it's a dev dependency name: 'bits-ui', // Import name to detect packageName: 'bits-ui@next', // NPM package name url: 'https://github.com/huntabyte/bits-ui' // Reference URL } // ... other dependencies ] as const;
Important
When using a new dependency in your component, you MUST add it to POSSIBLE_DEPENDENCIES
first.
This ensures the dependency detection system can find and document it correctly.
-
Adding New Dependencies
To add a new dependency:
- Open
src/lib/constants.ts
- Add an entry to
POSSIBLE_DEPENDENCIES
:{ dev: boolean, // true for devDependencies, false for dependencies name: string, // The import path to detect (e.g., 'your-package') packageName: string, // The exact package name with version (e.g., 'your-package@1.0.0') url: string // Package repository or documentation URL }
- Commit the change before adding components that use this dependency
- Open
-
How It Works
The system (
src/lib/utils/handleComponentSource.ts
):- Scans component source code for imports
- Matches imports against
POSSIBLE_DEPENDENCIES
- Generates installation commands
- Adds dependency information to component metadata
-
Example Output
When a component uses dependencies:
<script> import * as Accordion from 'bits-ui/accordion'; import { Icon } from '@iconify-json/ri'; </script>
The system automatically:
<!-- Dependencies: pnpm i -D @iconify-json/ri pnpm i bits-ui@next -->
-
Special Cases
-
Enhanced Images:
// Automatically detects and adds @sveltejs/enhanced-img const enhancedImageMatch = source.match(ENHANCED_IMAGE_REGEX);
-
Multiple Dependencies:
# Combines dev and runtime dependencies pnpm i -D @iconify-json/ri unplugin-icons && pnpm i bits-ui@next
-
-
Integration
The system integrates with:
- Component API endpoints
- Documentation generation
- Development tooling
Tip
When adding new components that use external packages:
- First add the dependency to
POSSIBLE_DEPENDENCIES
- Then create your component with the imports
- The system will automatically detect and document the dependencies
The project includes an automatic code collapsing system for better readability in the documentation. This feature is implemented in src/lib/utils/shiki-transformer/collapsible.ts
.
-
How to Use
Add collapse markers in your component code:
<script module lang="ts"> // [!code collapse-start] const longDataArray = [ // ... many items ]; // [!code collapse-end] </script>
-
Supported Comment Types
The system recognizes collapse markers, like this:
// JavaScript/TypeScript style // [!code collapse-start] // long code // [!code collapse-end]
-
How It Works
The system:
- Detects collapse markers in source code
- Wraps the content in a collapsible section
- Adds an "Expand/Collapse" button
- Preserves code highlighting and formatting
-
Example Use Case
Perfect for:
- Large data arrays
- Configuration objects
- Repetitive code sections
- Long utility functions
<script module lang="ts"> // [!code collapse-start] const items = [ { id: 1, value: '...' }, { id: 2, value: '...' } // ... many more items ]; // [!code collapse-end] </script> <script lang="ts"> // This code remains visible by default let selectedItem = items[0]; </script>
-
Best Practices
- Use for non-essential code sections
- Keep critical implementation details visible
- Collapse large data structures
- Use in documentation and examples
Tip
Use code collapsing to improve the readability of your components in the documentation while keeping all code accessible when needed.
-
1. Component Organization
-
1.1. Base (
/ui
) componentsBase components are in the
src/lib/components/ui
folder.Each component should be in its own directory if it needs multiple components.
component-category/ ├── index.ts # Exports ├── component.svelte # Main component
If a component is a simple one, it can be placed in the
src/lib/components/ui
folder.component-category/ └── component.svelte # Main component
-
1.2. Origin UI components
Origin UI components are in the
src/lib/components
folder.Each component should be in its own desired category (e.g.
inputs
,buttons
, etc.)component-category/ ├── category-01.svelte ├── category-02.svelte └── ...
If a component isn't achievable (e.g. not the necessary libraries available, not enough time to implement, etc.) you need to add a placeholder component in the
src/lib/components
folder. In this case the component should be namedcategory-03.todo.svelte
.component-category/ └── category-03.todo.svelte
If a component is just waiting for a library to be implemented, you need to add a placeholder component in the
src/lib/components
folder. In this case the component should be namedcategory-04.soon.svelte
.component-category/ └── category-04.soon.svelte
-
Note
The component should have content. For example:
<p class="text-center text-sm text-muted-foreground">
waiting for
<a class="underline hover:text-foreground" href="https://github.com/huntabyte/bits-ui/pull/582"
>Bits UI TimeField</a
>
</p>
-
1.3. Route Configuration
When adding a new component category, you must configure its routing in
src/lib/config/routes.ts
:{ componentDirectory: ['your-category'], // Directory name(s) containing components header: { description: 'A growing collection of ${count} components built with Svelte and TailwindCSS.', title: 'Your Category' }, label: 'Your Category', // Navigation label path: 'your-category', // URL path seo: { description: 'An extensive collection of copy-and-paste components built with Svelte and TailwindCSS.', keywords: 'your, keywords, component, svelte, tailwindcss', title: 'Your Category', twitterDescription: 'An extensive collection of copy-and-paste components built with Svelte and TailwindCSS.', twitterTitle: 'Your Category' } }
For combined categories (like inputs/textareas), use multiple directories:
componentDirectory: ['category1', 'category2'], path: 'combined-category'
-
Component Requirements
- Resemble the original component as closely as possible
- If there is a "better" way to implement the component, use that
- Use Svelte 5 runes for state management
- Include TypeScript types for props and events
- Implement proper ARIA attributes for accessibility
-
Styling Guidelines
- Use Tailwind CSS for all styling
- Follow the project's color scheme using CSS variables
- Implement responsive design using Tailwind breakpoints
- Use semantic HTML elements
- Maintain dark mode support
-
Before Starting
- Check existing issues and PRs to avoid duplicate work
- For new features, open an issue for discussion first
- Fork the repository and create a feature branch:
git checkout -b feature/your-feature-name
-
Component Development
- Follow the component organization guidelines
- Place components in appropriate directories
- Use the correct naming convention:
component-category/ ├── category-XX.svelte # Regular component ├── category-XX.todo.svelte # Not yet possible └── category-XX.soon.svelte # Waiting for dependency
- Run
pnpm generate:registry
after adding/modifying components
-
Quality Checklist
- Component matches original design
- Svelte 5 runes used correctly
- TypeScript types are complete
- ARIA attributes implemented
- Dark mode works correctly
- Responsive design tested
- No console errors/warnings
- Code formatted (
pnpm format
) - Linting passes (
pnpm lint
)
-
Route Configuration
- If adding a new category:
- Add entry to
src/lib/config/routes.ts
- Configure SEO metadata
- Add entry to
- For existing categories:
- Ensure component follows naming pattern
- Update component count in route description
- If adding a new category:
-
Documentation
- Include example usage
- Add placeholder content for
.todo
and.soon
components
-
Submitting the PR
git add . git commit -m "feat: add new component category" git push origin feature/your-feature-name
- Use conventional commit messages:
feat:
for new components/featuresfix:
for bug fixesdocs:
for documentationrefactor:
for code improvementschore:
for maintenance
- Use conventional commit messages:
Note
Large PRs are harder to review. Consider breaking big changes into smaller, focused PRs.
- Check existing issues and pull requests
- Join our discussions in the repository
- Open an issue for questions
We appreciate your contributions to the Svelte Community and to this project!
Feel free to use these components in personal and commercial projects. However, while the tutorials and demos are available for your use as-is, they cannot be redistributed or resold.
For any questions or feedback, please open an issue on this repository.
This project is a work in progress, and i am continuously working to improve and expand this collection.