The PreloadStateMixin allows you to perform asynchronous work on the server before the initial render. This is useful for fetching data from databases, APIs, or performing other async operations during server-side rendering.
Overview
PreloadStateMixin is a mixin on State that preloads state on the server before initState() is called.
Signature
mixin PreloadStateMixin < T extends StatefulComponent > on State < T >
The type of the component this state is associated with.
Method
preloadState() Called on the server before initState() to preload asynchronous data. @protected
Future < void > preloadState ()
Returns: A Future that completes when the data has been loaded.
preloadState() is only called on the server during the first build. It is not called on the client or during subsequent rebuilds.
Usage
Basic Example
import 'package:jaspr/jaspr.dart' ;
class UserProfile extends StatefulComponent {
const UserProfile ({ required this .userId});
final String userId;
@override
State createState () => UserProfileState ();
}
class UserProfileState extends State < UserProfile >
with PreloadStateMixin < UserProfile > {
Map < String , dynamic > ? userData;
@override
Future < void > preloadState () async {
// This runs on the server before rendering
userData = await fetchUserFromDatabase (component.userId);
}
@override
Component build ( BuildContext context) {
if (userData == null ) {
return div ([], [ text ( 'Loading...' )]);
}
return div ([], [
h1 ([], [ text (userData ! [ 'name' ])]),
p ([], [ text (userData ! [ 'email' ])]),
]);
}
Future < Map < String , dynamic >> fetchUserFromDatabase ( String id) async {
// Database fetch logic
await Future . delayed ( Duration (seconds : 1 ));
return { 'name' : 'John Doe' , 'email' : '[email protected] ' };
}
}
With API Calls
import 'dart:convert' ;
import 'package:http/http.dart' as http;
import 'package:jaspr/jaspr.dart' ;
class ArticleList extends StatefulComponent {
@override
State createState () => ArticleListState ();
}
class ArticleListState extends State < ArticleList >
with PreloadStateMixin < ArticleList > {
List < Map < String , dynamic >> articles = [];
@override
Future < void > preloadState () async {
final response = await http. get (
Uri . parse ( 'https://api.example.com/articles' ),
);
if (response.statusCode == 200 ) {
articles = ( jsonDecode (response.body) as List )
. cast < Map < String , dynamic >>();
}
}
@override
Component build ( BuildContext context) {
return div ([], [
h2 ([], [ text ( 'Articles' )]),
...articles. map ((article) =>
article ([], [
h3 ([], [ text (article[ 'title' ])]),
p ([], [ text (article[ 'summary' ])]),
])
),
]);
}
}
Lifecycle Order
When using PreloadStateMixin, the lifecycle order on the server is:
Component is mounted
preloadState() is called and awaited
initState() is called
build() is called
Component is rendered to HTML
class MyState extends State < MyComponent >
with PreloadStateMixin < MyComponent > {
String data = '' ;
@override
Future < void > preloadState () async {
print ( '1. preloadState called' );
data = await fetchData ();
print ( '2. preloadState completed' );
}
@override
void initState () {
super . initState ();
print ( '3. initState called' );
// data is already loaded here
}
@override
Component build ( BuildContext context) {
print ( '4. build called' );
return div ([], [ text (data)]);
}
}
Combining with SyncStateMixin
A common pattern is to preload data on the server and sync it to the client:
class DataComponent extends StatefulComponent {
@override
State createState () => DataComponentState ();
}
class DataComponentState extends State < DataComponent >
with PreloadStateMixin , SyncStateMixin < DataComponent , List < String >> {
List < String > items = [];
@override
Future < void > preloadState () async {
// Fetch data on the server
items = await fetchItemsFromDatabase ();
}
@override
List < String > getState () {
// Send data to the client
return items;
}
@override
void updateState ( List < String > value) {
// Receive data on the client
items = value;
}
@override
Component build ( BuildContext context) {
return ul ([],
items. map ((item) => li ([], [ text (item)])). toList (),
);
}
Future < List < String >> fetchItemsFromDatabase () async {
await Future . delayed ( Duration (milliseconds : 100 ));
return [ 'Item 1' , 'Item 2' , 'Item 3' ];
}
}
Error Handling
class DataState extends State < DataComponent >
with PreloadStateMixin < DataComponent > {
String ? data;
String ? error;
@override
Future < void > preloadState () async {
try {
data = await fetchData ();
} catch (e) {
error = e. toString ();
}
}
@override
Component build ( BuildContext context) {
if (error != null ) {
return div (classes : 'error' , [
text ( 'Error: $ error ' ),
]);
}
if (data == null ) {
return div ([], [ text ( 'Loading...' )]);
}
return div ([], [ text (data ! )]);
}
}
When to Use
Use PreloadStateMixin when you need to:
Fetch data from a database before rendering
Make API calls during server-side rendering
Perform any async operation that must complete before the initial render
Pre-compute expensive operations on the server
Do NOT use async/await directly in initState(). The framework will throw an error if initState() returns a Future. Use PreloadStateMixin instead.
Don’t Do This
class BadState extends State < BadComponent > {
String data = '' ;
@override
void initState () async { // ❌ Wrong!
super . initState ();
data = await fetchData (); // This will cause an error
}
@override
Component build ( BuildContext context) {
return div ([], [ text (data)]);
}
}
Do This Instead
class GoodState extends State < GoodComponent >
with PreloadStateMixin < GoodComponent > {
String data = '' ;
@override
Future < void > preloadState () async { // ✅ Correct!
data = await fetchData ();
}
@override
Component build ( BuildContext context) {
return div ([], [ text (data)]);
}
}
Alternatives
If you need to perform async work but don’t need it to complete before the initial render, consider:
AsyncStatelessComponent - For stateless components with async build
AsyncBuilder - For async operations in the component tree
FutureBuilder - For future-based async operations
StreamBuilder - For stream-based async operations
These alternatives allow async work to happen after the initial render, showing a loading state while waiting.
See Also