Appearance
Architecture & Philosophy
Flexible usage philosophy
Katalyst is designed to be adopted incrementally. You are not required to use every architectural pattern it provides.
Three ways to use Katalyst
Full behavioral framework (recommended for teams building complex admin surfaces)
- Use the complete architecture: DI, Clean Architecture layers, shared primitives
- Best for: large teams, long-lived products, apps with many similar screens
UI component system only
- Use only the shadcn-style UI primitives and shared components
- Skip DI, module structure, and behavioral hooks
- Best for: smaller projects, rapid prototypes, teams with existing architecture
Starting point to adapt freely
- Fork the structure and modify it to fit your needs
- Remove or replace any layer that doesn't serve you
- Best for: teams with strong opinions on architecture
What's optional
| Element | Purpose | Can skip if... |
|---|---|---|
| Dependency Injection | Testability, loose coupling | You prefer simpler imports or don't need DI |
| Clean Architecture layers | Separation of concerns | Your app is small or you have a different structure |
| Module structure | Feature isolation | You prefer flat or different organization |
| Behavioral hooks | Consistent interactions | You only need UI components |
| Infrastructure services | Cross-cutting concerns | You have existing solutions |
Gradual adoption path
If you're unsure, start simple and adopt patterns as needed:
- Start with UI — Use
src/shared/ui/primitives/for consistent styling - Add behavioral hooks — When you notice repeated interaction logic, use
src/shared/hooks/ - Adopt module structure — When features grow, organize into
src/modules/<feature>/ - Enable DI — When you need testability or swappable implementations
There's no penalty for keeping things simple. The architecture exists to help at scale, not to impose overhead on small projects.
Design goals
Katalyst exists to make common admin UI behavior predictable and reusable.
It intentionally solves:
- A consistent way to implement recurring interactions (selection, confirmation, inline edit, shortcuts, command palette)
- A shared UI foundation so screens look and behave similarly
- A structure where features can be added without rewriting app-level plumbing
It does not try to solve:
- Your product’s domain model (entities, permissions, business rules)
- Backend architecture, deployment, or hosting
- Every UI variant via configuration or theme systems
The goal is to keep the “rules of interaction” shared, while keeping product behavior local to features.
Primitives-first approach
Shared primitives are small, focused building blocks (primarily hooks) that encapsulate behavior you want to be consistent across the app.
This approach exists because:
- Centralizing behavior makes edge cases consistent (and easier to fix once)
- Shared primitives prevent “almost the same” implementations from drifting over time
- A stable primitive API makes patterns copy-safe across features
Duplication is avoided by default. If two screens solve the same interaction differently, you’ve created two maintenance paths.
Layers of the system
Katalyst is structured as a simple set of layers:
- Shared primitives
- Live in
src/shared/hooks/. - Own interaction state and control flow.
- Live in
- Thin components
- Live in
src/shared/ui/components/. - Render UI around primitives (for example, a dialog that renders state from a hook).
- Built on a shared primitive UI foundation in
src/shared/ui/shadcn/.
- Live in
- Feature modules
- Live in
src/modules/. - Own product-specific pages, flows, and domain behavior.
- Consume shared primitives/components rather than re-implementing them.
- Live in
- Showcase (consumer)
- Lives in
src/modules/showcase/. - Acts as a reference implementation and a “dogfooding” area for primitives and patterns.
- Lives in
Composition over configuration
Katalyst favors composition:
- Primitives are meant to be combined in code to produce the behavior you need.
- Patterns emerge from composing a small set of primitives, not from adding more configuration surface.
This is intentional. Configuration-heavy systems often hide behavior behind options and special cases. Composition keeps behavior explicit at the call site and easier to review.
Consistency and scalability
Consistency is enforced by how you build:
- Reuse shared primitives for behavior that must match across screens
- Keep UI components thin so they don’t become “mini frameworks”
- Treat patterns as reusable compositions, not new layers
When adding new features:
- Start inside a feature module (
src/modules/...) and compose existing primitives. - Prefer small wrappers local to the feature before creating shared abstractions.
When to introduce a new primitive:
- The behavior repeats across multiple features
- The behavior has meaningful edge cases you want solved once
- You can describe the API without referencing a specific domain entity
When not to introduce a new primitive:
- The behavior is specific to one feature or one domain concept
- The API would be mostly configuration and special cases
- You can’t yet name the invariant you’re trying to standardize
What to do when extending Katalyst
Use this checklist to stay aligned:
- Start with intent
- Identify what must be consistent (interaction behavior) vs what is product-specific (domain rules).
- Search for existing building blocks
- Prefer reusing a shared primitive over re-implementing the same interaction.
- Keep logic and UI separate
- Put reusable behavior in primitives.
- Keep components as render layers over that behavior.
- Prove reuse before sharing
- If you think something should be shared, validate that it applies to more than one feature.
- Use Showcase as the reference consumer
- If a primitive/pattern is meant to be reused, it should be demonstrable in
src/modules/showcase/.
- If a primitive/pattern is meant to be reused, it should be demonstrable in
This keeps features consistent without forcing everything into a single global abstraction.