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:

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:

  1. When using package:web you should instead use package:universal_web which already works across web and server environments. No need to use conditional imports or the @Import annotation shown below.
  2. 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.
  3. 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 and AsyncBuilder 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:

  1. Your apps entrypoint is main.dart. This is only executed on the server. Hence all components that are rendered from there (the root Document component and all its descendents) are rendered on the server.
  2. Go through the component tree top down, starting at the root component and moving downwards through its descendents.
  3. 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:

  1. Start at the component you want to check.
  2. If it or one of its ancestors is a client component, it is rendered on both the server and client.
  3. 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:

  1. Components rendered only on the server can safely:
  • use server-side Jaspr features like Document() and AsyncStatelessComponent.
  • import and use server-specific libraries like dart:io.
  • access the filesystem, connect to a database etc.
  1. 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: