Introduction to Pulsar Web
Learn about Pulsar Web Framework.
Introduction
Why Pulsar
Most modern web frameworks optimize for speed of iteration rather than long-term clarity. As applications grow, UI logic often becomes fragmented across implicit abstractions, hidden state flows, and short-lived components.
Pulsar exists as a response to that complexity. It favors explicit architecture, predictable behavior, and long-lived UI objects over framework magic and convenience-driven design.
The Problem with Implicit Abstractions
Consider how components are typically managed in modern frameworks:
// Typical framework: Components are functions
Widget dashboard() {
final analytics = AnalyticsWidget(); // Created every render
final chart = ChartWidget(); // No identity preservation
return Column([analytics, chart]);
}
// Where does state live?
// How do components communicate?
// When are components created vs. reused?
// All implicit, all hidden.
In Pulsar, these questions have explicit answers:
// Pulsar: Components are objects with identity
final class DashboardPage extends Component {
final AnalyticsWidget analytics = AnalyticsWidget(); // Created once
final ChartWidget chart = ChartWidget(); // Identity preserved
@override
Morphic render() {
return Div()([analytics, chart]);
}
}
// State lives in the object.
// Components communicate via constructors and callbacks.
// Components are created when you create them, reused when you store them.
// Everything explicit, everything visible.
What is Pulsar
Pulsar is a Dart-first web framework for building structured and maintainable web applications. Components in Pulsar are objects, not functions. They preserve identity, hold their own state, and evolve over time instead of being recreated on every render.
Pulsar does not try to abstract the web away — it embraces it. HTML, CSS, and browser behavior remain visible and explicit.
Core Characteristics
- Components as Objects — Long-lived instances with identity and lifecycle.
- Explicit State Management — State lives in component fields, updated via morph().
- Pure Description — render() is a pure function that describes structure.
- Domain Oriented UI — Business logic and styling logic live in domain objects.
- Type Safety First — Enums over strings, objects over maps.
- Web-Native — Standard HTML elements, CSS, and browser APIs.
A Simple Example
import 'package:pulsar_web/pulsar.dart';
final class Counter extends Component {
int count = 0; // State is just a field
void increment(Event e) {
morph(() => count++); // Explicit state update
}
@override
Morphic render() {
// Pure description of structure
return Div().classes("counter")([
H1()(['Count: $count']),
Button().onClick(increment)(['Increment']),
]);
}
}
void main() {
mountApp(Counter());
}
No hooks. No special rules. No implicit behavior. Just objects, methods, and explicit state updates. Domain Oriented UI (DOU)
Pulsar introduces Domain Oriented UI (DOU) — a methodology that applies Domain-Driven Design principles to frontend development.
In DOU, components are focused on structure and presentation, while business logic and styling logic are isolated into domain objects.
Without DOU: Logic in Components
// Component handles everything
final class ShoppingCart extends Component {
List<CartItem> items = [];
double get subtotal {
return items.fold(0.0, (sum, item) => sum + item.price * item.quantity);
}
double get tax => subtotal * 0.1;
double get total => subtotal + tax;
void addItem(CartItem item) {
morph(() => items.add(item));
}
String get cartClasses {
final base = 'cart';
final emptyClass = items.isEmpty ? 'cart-empty' : '';
return '$base $emptyClass';
}
@override
Morphic render() {
return Div().classes(cartClasses)([
P()(['Total: $${total.toStringAsFixed(2)}']),
// ... complex render logic
]);
}
}
With DOU: Logic in Domain Objects
// Domain object: Business logic
class ShoppingCart {
List<CartItem> items = [];
double get subtotal =>
items.fold(0.0, (sum, item) => sum + item.price * item.quantity);
double get tax => subtotal * 0.1;
double get total => subtotal + tax;
void addItem(CartItem item) {
items.add(item);
}
}
// Domain object: Styling logic
class CartStyles {
final bool isEmpty;
CartStyles({required this.isEmpty});
String get classes => isEmpty ? 'cart cart-empty' : 'cart';
}
// Component: Pure structure
final class ShoppingCartView extends Component {
final ShoppingCart cart = ShoppingCart();
CartStyles get styles => CartStyles(isEmpty: cart.items.isEmpty);
void handleAddItem(CartItem item) {
morph(() => cart.addItem(item));
}
@override
Morphic render() {
return Div().classes(styles.classes)([
P()(['Total: $${cart.total.toStringAsFixed(2)}']),
// ... focused on structure only
]);
}
}
The component becomes a thin layer that connects domain logic to the UI. Business rules and styling decisions are testable, reusable, and isolated.
DOU in Practice: Domain objects can be tested without rendering a single UI element. Components become predictable coordinators rather than complex business logic containers.
A Clear Point of View
Pulsar is opinionated by design. It prioritizes clarity over convenience, explicitness over flexibility, and architecture over short-term ergonomics.
What Pulsar Values
- Clarity over cleverness — Code should be obvious, not smart.
- Explicit over implicit — Dependencies and data flow must be visible.
- Objects over functions — Components are long-lived objects with identity.
- Domain separation — Business logic lives in domain objects, not components.
- Type safety — Enums over strings, compile-time checks over runtime errors.
- Web-native — HTML, CSS, and standard browser APIs.
What Pulsar Avoids
- Framework magic — No hidden behavior, no implicit state updates.
- Global state management — State lives in components or domain objects.
- Hooks and special rules — State is just fields, updates via morph().
- Virtual DOM abstractions — Components describe real DOM structure.
- Convention over configuration — Explicit decisions over hidden defaults.
Who Pulsar is For
Pulsar is designed for developers and teams who:
- Value long-term maintainability over short-term convenience
- Want explicit architecture that stays understandable at scale
- Prefer testable, isolated domain logic
- Believe in the power of objects and composition
- Are building applications that will be maintained for years
- Want to understand what their framework is doing
If you value predictable UI behavior, clear architectural boundaries, and explicit design, Pulsar gives you a framework that stays understandable as your application grows.
How to Think About Pulsar
Pulsar is not React with Dart syntax. It is not Flutter for the web. It is a deliberate architectural approach to building web applications.
Think of Pulsar as:
- A component system — where components are objects with identity and lifecycle.
- A state management philosophy — where state is explicit, local, and updated deliberately.
- A domain modeling tool — where business logic lives in domain objects, not UI code.
- A web-native framework — that embraces HTML, CSS, and browser APIs rather than hiding them.
The framework guides you toward patterns that scale: explicit dependencies, isolated logic, and predictable behavior. These patterns may feel verbose at first, but they pay dividends as applications grow.
What You Will Learn
This documentation will teach you:
- How components work as long-lived objects
- How to manage state explicitly with morph()
- How to isolate business logic into domain objects
- How to build routing, styling, and event handling
- How to think in terms of structure, not effects
- Why every design decision was made the way it was
By the end, you will understand not just how to use Pulsar, but why it works the way it does.
Next Steps: Start with the Philosophy section to understand Pulsar's core principles, then move to Mental Model to learn how to think in Pulsar. The rest of the documentation builds from these foundations.