Server vs Client
Understand the difference between server and client rendering.
This page is only relevant when using static or server mode.
When using Jaspr in static or server mode, your app will be executed both on the server (as native code or running in the Dart VM) and on the client (compiled to either JS or WASM), with some parts being run in both environments and some in only one.
As a result, some or your components will be:
- only rendered on the server
- rendered both on the server and client
- only rendered on the client (almost never happens though).
The terms "server" and "client" in this context refer to the environment in which the code is executed, not the actual hardware running it. Specifically, the "server" environment is the same whether you run it on your local machine (during development), a CI pipeline (in "static" mode) or an actual webserver (in "server" mode).
Learning about and understanding the differences between these environments, and what causes a component to be rendered in which environment is crucial to building a successful app with Jaspr.
Difference between Server and Client
The main difference between the server and client environments are the platform libraries from the Dart SDK that are available:
- The server environment has access to Native platform libraries
- The client environment has access to Web platform libraries
Therefore, accessing a web-specific library like dart:js_interop
in a server-rendered component will result in a compilation error.
If you must access one of these libraries in a component that is rendered in both environments, you have several options:
- When using
package:web
you should instead usepackage:universal_web
which already works across web and server environments. No need to use conditional imports or the@Import
annotation shown below. - You can use the
@Import
annotation to import the correct library depending on the environment. This is a shorthand for using conditional imports and is the recommended way to do this in Jaspr. - You can use Dart's conditional imports to import the correct library depending on the environment. This is a more verbose way of doing the same thing as the
@Import
annotation, but is still valid.
Additionally, there are some Jaspr features that are only available in one of the environments. For example:
- Any
event
handlers will only be fired on the client. setState()
may only be called on the client.- The
Document()
component is only available on the server. - The
AsyncStatelessComponent
andAsyncBuilder
components are only available on the server.
Where is my component rendered?
To write components that works in one or both environments, you need to understand how Jaspr decides where to render your components (or how you control it).
By default, you can reason about your components as follows:
- Your apps entrypoint is
main.dart
. This is only executed on the server. Hence all components that are rendered from there (the rootDocument
component and all its descendents) are rendered on the server. - Go through the component tree top down, starting at the root component and moving downwards through its descendents.
- Any component annotated with
@client
is a client component. In addition to being rendered on the server, it is also attached as a root component on the client. This means:
- It will be rendered both on the server and client.
- All of its descendents are also rendered both on the server and client.
To find out if a particular component is rendered on the server or client, you can turn this around and move up the tree:
- Start at the component you want to check.
- If it or one of its ancestors is a client component, it is rendered on both the server and client.
- Else, it is only rendered on the server.
Therefore @client
components act as a sort of boundary. Everything below in the tree is rendered in both environments, while everything above is only rendered on the server.
To reiterate:
- Components rendered only on the server can safely:
- use server-side Jaspr features like
Document()
andAsyncStatelessComponent
. - import and use server-specific libraries like
dart:io
. - access the filesystem, connect to a database etc.
- Components rendered both on the server and client can:
- use client-side Jaspr features like
setState()
. - use
kIsWeb
to check if the code is running on the client. - neither import client- nor server-specific libraries directly. Instead, they must use one of the options mentioned above to import the correct library depending on the environment.
For more info regarding this topic, check out the following pages: