A lightweight, reactive TypeScript UI framework with advanced animation capabilities built-in
โก Simple โข ๐ Reactive โข ๐จ Animated โข ๐ง Powerful โข ๐ Type-Safe
Live Demo โข Documentation โข Discord
Singular is designed for developers who want React's developer experience with better performance and built-in animations โ without the complexity of a virtual DOM. Built with TypeScript from the ground up, Singular provides excellent type safety and IntelliSense support.
// This is all you need for a reactive, animated counter
import { createApp, createElement, useState } from 'singular-framework';
import { animateOnClick } from 'singular-framework/animate';
function Counter() {
const [count, setCount] = useState(0);
return createElement('div', {},
createElement('h1', {}, () => `Count: ${count()}`),
createElement('button', {
onclick: () => setCount(count() + 1),
ref: el => animateOnClick(el, {
transform: ['scale(1)', 'scale(0.95)'],
duration: 100
})
}, 'Click me!')
);
}
createApp(Counter).mount('#app');No virtual DOM, no unnecessary re-renders. Updates are surgical and precise, inspired by SolidJS.
Scroll animations, hover effects, click feedback, loops, sequences โ all included out of the box.
~12KB gzipped with the full animation system. Compare that to React's 42KB (without animations).
Built entirely in TypeScript with comprehensive type definitions. Get full IntelliSense and type safety throughout your application.
Functional components, context API, lifecycle hooks, and control flow primitives.
Built-in routing with nested routes, guards, and animated page transitions.
TypeScript-first with full type definitions, HMR support, comprehensive devtools, and extensive documentation.
npm install singular-framework
# or
yarn add singular-framework
# or
pnpm add singular-frameworkimport { createApp, createElement, useState } from 'singular-framework';
function App() {
const [message, setMessage] = useState('Hello Singular!');
return createElement('div', { className: 'app' },
createElement('h1', {}, () => message()),
createElement('button', {
onclick: () => setMessage('Welcome to reactive programming!')
}, 'Change Message')
);
}
createApp(App).mount('#app');import { createApp, createElement, useState } from 'singular-framework';
interface Todo {
id: number;
text: string;
completed: boolean;
}
function TodoApp() {
const [todos, setTodos] = useState<Todo[]>([]);
const [input, setInput] = useState<string>('');
const addTodo = () => {
const newTodo: Todo = {
id: Date.now(),
text: input(),
completed: false
};
setTodos([...todos(), newTodo]);
setInput('');
};
return createElement('div', { className: 'todo-app' },
createElement('input', {
type: 'text',
value: () => input(),
oninput: (e: Event) => setInput((e.target as HTMLInputElement).value)
}),
createElement('button', { onclick: addTodo }, 'Add Todo'),
...todos().map(todo =>
createElement('div', { key: todo.id }, todo.text)
)
);
}
createApp(TodoApp).mount('#app');import { createApp, createElement } from 'singular-framework';
import { animateOnScroll, animateOnHover } from 'singular-framework/animate';
interface CardProps {
title: string;
content: string;
}
function AnimatedCard({ title, content }: CardProps) {
return createElement('div', {
className: 'card',
ref: (el: HTMLElement) => {
// Animate in on scroll
animateOnScroll(el, {
opacity: ['0', '1'],
transform: ['translateY(30px)', 'translateY(0)'],
duration: 600,
once: true
});
// Add hover effect
animateOnHover(el, {
transform: ['scale(1)', 'scale(1.02)'],
duration: 200,
reverseOnLeave: true
});
}
},
createElement('h2', {}, title),
createElement('p', {}, content)
);
}Singular's animation system is one of its killer features. No external libraries needed.
| Function | Description | Use Case |
|---|---|---|
animate() |
Basic animation | Manual control |
animateOnScroll() |
Trigger on viewport entry | Fade-in effects |
animateOnHover() |
Mouse enter/leave | Interactive feedback |
animateOnClick() |
Click feedback | Button presses |
animateLoop() |
Continuous loop | Loading spinners |
animateSequence() |
Chained animations | Complex transitions |
animateStagger() |
Multiple elements with delay | List animations |
import {
animate,
animateSequence,
animateStagger,
fadeIn,
bounce
} from 'singular-framework/animate';
// Fade in element
fadeIn(element, 400);
// Bounce effect
bounce(element, 15, 600); // 15px intensity, 600ms duration
// Chain animations
animateSequence(element, [
{ transform: ['translateX(0)', 'translateX(100px)'], duration: 500 },
{ transform: ['scale(1)', 'scale(1.2)'], duration: 300 },
{ opacity: ['1', '0'], duration: 200 }
]);
// Stagger animation for lists
const cards = document.querySelectorAll('.card');
animateStagger(cards, {
opacity: ['0', '1'],
transform: ['translateY(20px)', 'translateY(0)'],
duration: 400
}, 100); // 100ms delay between eachconst easings = {
linear,
easeOutCubic,
easeInOutQuad,
easeOutQuart,
easeInCubic,
easeInOutCubic,
easeOutBack
};import { useState, effect, computed } from 'singular-framework';
const [count, setCount] = useState<number>(0);
const [multiplier, setMultiplier] = useState<number>(2);
// Computed values
const result = computed(() => count() * multiplier());
// Side effects
effect(() => {
console.log(`Result: ${result()}`);
});
// Batch updates
import { batch } from 'singular-framework';
batch(() => {
setCount(10);
setMultiplier(5);
// Effect runs once with both updates
});interface Todo {
id: number;
text: string;
}
function TodoList() {
const [todos, setTodos] = useState<Todo[]>([]);
const [input, setInput] = useState<string>('');
const addTodo = () => {
setTodos([...todos(), { id: Date.now(), text: input() }]);
setInput('');
};
return createElement('div', {},
createElement('input', {
value: () => input(),
oninput: (e: Event) => setInput((e.target as HTMLInputElement).value)
}),
createElement('button', { onclick: addTodo }, 'Add'),
...todos().map(todo =>
createElement('div', { key: todo.id }, todo.text)
)
);
}interface ButtonProps {
children: string;
variant?: 'primary' | 'secondary';
onclick?: () => void;
}
function Button({ children, variant = 'primary', onclick }: ButtonProps) {
return createElement('button', {
className: `btn btn-${variant}`,
onclick
}, children);
}
function App() {
return createElement('div', {},
Button({
children: 'Primary Button',
onclick: () => alert('Clicked!')
}),
Button({
children: 'Secondary',
variant: 'secondary',
onclick: () => console.log('Secondary clicked')
})
);
}import { createContext } from 'singular-framework';
type Theme = 'light' | 'dark';
const ThemeContext = createContext<Theme>('light');
function ThemedButton() {
const theme = ThemeContext.Consumer();
return createElement('button', {
className: () => `btn theme-${theme()}`
}, 'Themed Button');
}
function App() {
const [theme, setTheme] = useState<Theme>('light');
return ThemeContext.Provider({
value: theme,
children: [
ThemedButton(),
createElement('button', {
onclick: () => setTheme(theme() === 'light' ? 'dark' : 'light')
}, 'Toggle Theme')
]
});
}import { Show, For } from 'singular-framework';
interface Item {
id: number;
name: string;
}
// Conditional rendering
Show({
when: () => isLoggedIn(),
children: [createElement('div', {}, 'Welcome back!')]
});
// List rendering
For({
each: () => items(),
children: (item: Item) => createElement('div', {}, item.name)
});import { Router, Link } from 'singular-framework';
const routes = [
{ path: '/', component: HomePage },
{ path: '/about', component: AboutPage },
{
path: '/user/:id',
component: UserPage,
beforeEnter: (to, from) => {
// Route guard
return isAuthenticated();
}
}
];
function App() {
return createElement('div', {},
createElement('nav', {},
Link({ to: '/', children: ['Home'] }),
Link({ to: '/about', children: ['About'] })
),
Router({ routes })
);
}| Framework | Core Size | With Animations |
|---|---|---|
| Singular | ~8KB | ~12KB |
| React | ~42KB | +external lib needed |
| Vue | ~35KB | +external lib needed |
| Svelte | ~10KB | +external lib needed |
- โ No Virtual DOM - Direct DOM updates
- โ Fine-grained reactivity - Only affected nodes update
- โ Throttled scroll events - 60fps guaranteed
- โ Intersection Observer - Efficient scroll animations
- โ RequestAnimationFrame - Smooth animations
- โ Automatic cleanup - No memory leaks
singular/
โโโ src/
โ โโโ index.ts # Main exports
โ โโโ reactivity.ts # useState, effect, computed
โ โโโ createElement.ts # DOM creation & bindings
โ โโโ render.ts # App mounting
โ โโโ components.ts # Show, For, Context
โ โโโ router.ts # Client-side routing
โ โโโ store.ts # State management
โ โโโ animate.ts # Animation system
โ
โโโ examples/
โ โโโ counter-app/ # Full demo with animations
โ โโโ todo-app/ # Todo list with routing
โ โโโ dashboard/ # Complex app example
โ
โโโ tests/ # Test suites
โโโ docs/ # Documentation
git clone https://github.com/singular-framework/core.git
cd singular
pnpm install
pnpm buildpnpm --filter counter-app dev # Animation demo
pnpm --filter todo-app dev # Todo apppnpm test # Run tests
pnpm test:watch # Watch mode
pnpm test:ui # UI runner- Core reactivity system
- Component architecture
- Advanced animation system
- Router with guards
- Context API
- Theme system
- Performance optimizations
- JSX support via Babel plugin
- Server-side rendering (SSR)
- Browser devtools extension
- Form handling utilities
- Gesture animations (touch/swipe)
-
create-singular-appCLI - Plugin system
- Static site generation
- Mobile renderer (React Native style)
- WebGL 3D animations
We welcome contributions! Here's how to get started:
# Fork and clone
git clone https://github.com/YOUR_USERNAME/Singular.git
cd singular
# Install dependencies
pnpm install
# Create feature branch
git checkout -b feature/awesome-feature
# Make changes, test, and build
pnpm test
pnpm build
# Test in example apps
pnpm --filter counter-app dev
# Submit PRSee CONTRIBUTING.md for detailed guidelines.
MIT ยฉ Singular Core Team
- ๐ Documentation
- ๐ฎ Live Demo
- ๐ฆ NPM Package
- ๐ฌ Discord Community
- ๐ฆ Twitter
- ๐ Issue Tracker
If Singular helps you build better apps, give us a โญ๏ธ on GitHub!
Built with โค๏ธ and โจ by the Singular community