Components
Jaspr uses a component-based architecture very similar to Flutter's widgets. Concepts like ui composition, architecture and state management are transferable.
- StatelessComponent: For components that don't need mutable state. You must override
Component build(BuildContext context). - StatefulComponent: For components with mutable state. Requires an associated
Stateclass. The state has lifecycle methods likeinitState()anddispose(). You must overrideComponent build(BuildContext context)in the state class. - InheritedComponent: For propagating context or state efficiently down the component tree.
Returning Components from build
Building UIs in Jaspr requires you to return a single Component from build().
- Rule 1: You MUST NOT use
Iterable<Component> build(BuildContext context) sync*. This is legacy code. - Rule 2: You MUST use dot-shorthands instead of capitalized component names for fragments, text, and empty nodes.
- Use
.fragment([...])(Do NOT useFragment([...])orfragment([...])). - Use
.text('...')(Do NOT useText('...')ortext('...')). - Use
.empty()to return an empty space safely.
- Use
Example Usage:
dartimport 'package:jaspr/jaspr.dart'; import 'package:jaspr/dom.dart'; class MyComponent extends StatelessComponent { const MyComponent({super.key}); @override Component build(BuildContext context) { // 1. Return a single component (e.g. div) // 2. Use dot-shorthand (.text) instead of Text() return div(classes: 'my-class', [ .text('Hello World'), ]); } }
HTML Components
Jaspr provides typed components for standard HTML elements (e.g., div(), p(), a(), button()). To use these add the package:jaspr/dom.dart import.
All HTML components take standard named Key? key, String? id, String? classes, Styles? style, Map<String, String>? attributes and Map<String, void Function(Event)>? events parameters.
Most HTML components take a positional List<Component> children parameter (except for self-closing tags like img, input, br, etc.).
- Rule 1: ALWAYS put the
childrenlist LAST, after all named parameters. - Rule 2: You MUST prefer available typed parameters (e.g.,
href,src,onClick) over using the rawattributes:orevents:maps. - Rule 3: When you are unsure about which typed parameters exist for an HTML component, you MUST read the respective reference file provided alongside this skill:
references/html/<tag>.mdcontains the full signature and example usage of the component for the given tag. (e.g.references/html/div.mdfordiv(),references/html/button.mdforbutton(), etc.)
- Rule 4: When a respective reference file does not exist for a tag (and therefore the component itself doesn't exist), you MUST use the generic
.element(tag: '...', /* other standard params, */ children: [ /* ... */ ])constructor instead.
Example Usage:
dartimport 'package:jaspr/jaspr.dart'; import 'package:jaspr/dom.dart'; class MyHtmlComponent extends StatelessComponent { const MyHtmlComponent({super.key}); @override Component build(BuildContext context) { return div(id: 'my-container', classes: 'my-class', [ p(attributes: {'aria-label': 'Example Paragraph'}, [ .text('Hello World'), ]), // E.g. signature as found at 'references/html/a.md' a(href: 'https://example.com', [ .text('Click me'), ]), ]); } }
Styling Components
Jaspr has built-in support for styling components using CSS-in-Dart. See the jaspr-styling skill for more information.
Interactivity and Events
1. Accessing Browser APIs
- Rule 1: In server or static mode, you MUST use
package:universal_web/web.dartto access browser APIs andpackage:universal_web/js_interop.dartto access js interop APIs. - Rule 2: When using
package:universal_webin server or static mode, you MUST wrap all API calls in anif (kIsWeb)check to prevent crashing the server render. - Rule 3: In client mode, you can use
dart:js_interopandpackage:webdirectly. - Rule 4: For global events, you MUST use
web.EventStreamProvidersto listen to events on thewindowordocumentas they provide a typed DartStreamof events.
dart// Example of safe usage in server/static mode: import 'package:universal_web/web.dart' as web; void logSize() { if (kIsWeb) { print('Window size: ${web.window.innerWidth}x${web.window.innerHeight}'); // Example of global event listener final sub = web.EventStreamProviders.resizeEvent.forTarget(web.window).listen((event) { print('Window resized'); }); } }
2. Handling Events
All DOM components support an events: parameter, and interactive components feature typed event callbacks (like onClick, onChange).
- Rule 1: You MUST use
web.Eventwhen typing raw events. - Rule 2: Use the
events()helper function for type-safe callback creation when needed.
dartimport 'package:jaspr/jaspr.dart'; import 'package:jaspr/dom.dart'; import 'package:universal_web/web.dart' as web; class MyButton extends StatelessComponent { @override Component build(BuildContext context) { return div( // Using raw events events: {'click': (web.Event event) { print('Div clicked'); }}, [ // Using typed parameters button( onClick: () => print('Button clicked'), [.text('Click me')] ) ] ); } }
3. Accessing Elements (GlobalNodeKey)
If you need direct reference to an underlying DOM element rendered by Jaspr, you can assign it a GlobalNodeKey.
dartimport 'package:jaspr/jaspr.dart'; import 'package:jaspr/dom.dart'; import 'package:universal_web/web.dart' as web; class MyInput extends StatefulComponent { @override State<MyInput> createState() => _MyInputState(); } class _MyInputState extends State<MyInput> { final GlobalNodeKey<web.HTMLInputElement> inputKey = GlobalNodeKey(); void focusInput() { inputKey.currentNode?.focus(); } @override Component build(BuildContext context) { return input(key: inputKey, type: .text); } }
Further Resources
- For information on pre-rendering, async data fetching, hydration, and the
@clientannotation see the related skill: jaspr-pre-rendering-and-hydration. - For information on styling components see the related skill: jaspr-styling.
- For information on how to convert HTML to Jaspr code, see the related skill: jaspr-convert-html.
- An index of all available documentation can be found at https://jaspr.site/llms.txt.


