The container is the primary unit of encapsulation in the Fluid Framework. A container is represented by the FluidContainer type and consists of a collection of shared objects and APIs to manage the lifecyle of those objects.

This article will explain:

  • How to create and load containers.
  • The APIs to interact with them.
  • The container lifecycle.

Creating & loading

Your code creates containers using APIs provided by a service-specific client library. Each service-specific client library implements a common API for manipulating containers. For example, the Tinylicious library provides these APIs for the Tinylicious Fluid service. These common APIs enable your code to define the container schema (which specifies what kinds of shared objects are in the container) and retrieve the FluidContainer object.

Container schema

Your code must define a schema that represents the structure of the data within the container. A schema can specify:

  • Some initial shared objects that are created as soon as the container is created, and are immediately and always available to all connected clients.
  • The types of shared objects that can be added to the container at runtime and persisted in the container for use by all connected clients.

The same schema definition that is used to create the container must be provided when clients subsequently load the container. For more information about initial objects and dynamic object creation see Data modeling.

This example schema defines two initial objects, layout and text, and declares the distributed data structures (DDSes) SharedCell and SharedString as shared object types that can be created at runtime.

const schema = {
    initialObjects: {
        layout: SharedMap,
        text: SharedString
    dynamicObjectTypes: [ SharedCell, SharedString ],

Creating a container

Containers are created from the service-specific client’s createContainer function. Your code must provide a configuration that is specific to the service and a schema object that defines the container schema.

About this code note:

  • client represents an object defined by the service-specific client library. See the documentation for the service you are using for more details about how to use its service-specific client library.
  • It is a good practice to deconstruct the object that is returned by createContainer into its two main parts; container and services. For an example of the use of the latter, see Working with the audience.
 1const schema = {
 2    initialObjects: {
 3        layout: SharedMap,
 4    },
 7const { container, services } =
 8    await client.createContainer(schema);
10const containerId = await container.attach();

Attaching a container

A newly created container is in a detached state. This is the point where you can create initial data to populate your shared objects if needed. A detached container is not connected to the Fluid service and no data is shared with other clients.

In order to attach the container to a service, call its attach method. Once attached, the Fluid container is connected to the Fluid service and can be loaded by other clients.

Invoking the container’s attach method returns the unique identifier for the container. To load an existing container, this value is used to identify the correct container to load.

Note that once attached, a container cannot be detached. Attach is a one-way operation. When loading an existing container, the loaded container is always attached.

 1const schema = {
 2    initialObjects: {
 3        layout: SharedMap,
 4    },
 7const { container, services } =
 8    await client.createContainer(schema);
10const containerId = await container.attach();

Loading a container

To load the container created in the above section your code calls the client’s getContainer method. The call must pass the id of the container to load as well as the exact same schema definition used when creating the container. The same container schema is required on all subsequent loads or the container will not be loaded correctly.

Note that when loading an existing container, the container is already attached.

1const schema = {
2    initialObjects: {
3        layout: SharedMap,
4    },
6const { container, services } =
7    await client.getContainer(/*container id*/, schema);

Deleting a container

Deleting a container is a service-specific feature, so you should refer to the documentation associated with the specific Fluid service you are using. See Available Fluid Services for more information about Fluid service options.

Container lifecycle and manipulation


Newly created containers begin in a detached state. A detached container is not connected to the Fluid service and no data is shared with other clients. This is an ideal time to create initial data in your Fluid data model.

An attached container, conversely, is connected to the Fluid service, and data changes are shared with the other clients. An existing container being loaded by a client will always initially be in an attached state.


The container exposes the connected state of the client and emits connected and disconnected events to notify the caller if the underlying connection is disrupted. Fluid will by default attempt to reconnect in case of lost/intermittent connectivity. However, connect() and disconnect() can be used to manually control the connection policy.

Note about this code:

  • user.on("idle") and user.on("active") are hypothetical event callbacks which fire if a user becomes idle or active respectively. They are for demonstration purposes only and are not provided by FluidFramework.
 1const connectionState = container.connectionState;
 3container.on("disconnected", () => {
 4    // handle disconnected
 5    // prevent the user from editing to avoid data loss
 8container.on("connected", () => {
 9    // handle connected
10    // enable editing if disabled
13user.on("idle", () => {
14    // Disconnect the container when idle to prevent unnecessary network traffic/COGS
15    container.disconnect();
18user.on("active", () => {
19    // Connect the container when active to resume op processing
20    container.connect();


The container object may be disposed to remove any server connections and clean up registered events. Once a container is disposed, your code must call getContainer again if it needs to be reloaded.

3const disposed = container.disposed;
5container.on("disposed", () => {
6    // handle event cleanup to prevent memory leaks


A container is considered dirty if it has local changes that have not yet been acknowledged by the service. You should always check the isDirty flag before closing the container or navigating away from the page. Closing the container while isDirty === true may result in the loss of operations that have not yet been acknowledged by the service.

A container is considered dirty in the following cases:

  1. The container has been created in the detached state, and either it has not been attached yet or it is in the process of being attached (container is in attaching state). If container is closed prior to being attached, the host may never know if the file was created or not.
  2. The container was attached, but it has local changes that have not yet been saved to the service endpoint. This occurs as part of normal op flow where pending operation (changes) are awaiting acknowledgement from the service. In some cases this can be due to lack of network connection. If the network connection is down, it needs to be restored for the pending changes to be acknowledged.

In terms of user experience, keep in mind that the isDirty flag will flicker as pending changes are awaiting acknowledgement. Therefore, the host may choose to incorporate some delay before interpreting the flag again, when basing the user experience off of its state.

1if(container.isDirty) {
2  container.on("saved", () => {
3    // safe to close the container
4  });


The container emits the saved event to notify the caller that all the local changes have been acknowledged by the service and the document is marked as clean.

1container.on("saved", () => {
2    // allow the user to edit


The container emits the dirty event to notify the caller that there are local changes that have not been acknowledged by the service yet and the document is still in a dirty state.

1container.on("dirty", () => {
2    // prevent the user from editing to avoid data loss

Initial objects

Initial objects are shared objects that your code defines in a container’s schema and which exist for the lifetime of the container. These shared objects are exposed via the initialObjects property on the container.

 1const schema = {
 2    initialObjects: {
 3        layout: SharedMap,
 4        text: SharedString
 5    },
 8// ...
10const layout = container.initialObjects.layout;
11const text = container.initialObjects.text;

For more information about initial objects see Data modeling.


The container also exposes a create function that enables creation of shared objects at runtime.

For more information about dynamic object creation see Data modeling.

Patterns for managing container lifecycle

Create/load separation

When creating and loading a container, it can be tempting to have a consistent code path for both creation and loading.

However, we generally recommend that creating and loading containers be separated. This provides a cleaner separation of responsibilities within the code itself. Also, in typical use-cases, a user will create a new container through some UI action that results in a redirect to another page whose sole responsibility is to load a container. All subsequent users will load the container by navigating directly to that page.

The drawback of this approach is that when creating a container, the service connection needs to be established twice – once for the container creation and once for the load. This can introduce latency in the container creation process. For an example of a simple scenario in which it makes sense to combine the flows, see Using Fluid with React.

Multi-container example

Multiple Fluid containers can be loaded from an application or on a Web page at the same time. There are two primary scenarios where an application would use multiple containers.

First, if your application loads two different experiences that have different underlying data structures. Experience 1 may require a SharedMap and Experience 2 may require a SharedString. To minimize the memory footprint of your application, your code can create two different container schemas and load only the schema that is needed. In this case your app has the capability of loading two different containers (two different schemas) but only loads one for a given user.

A more complex scenario involves loading two containers at once. Containers serve as a permissions boundary, so if you have cases where multiple users with different permissions are collaborating together, you may use multiple containers to ensure users have access only to what they should. For example, consider an education application where multiple teachers collaborate with students. The students and teachers may have a shared view while the teachers may also have an additional private view on the side. In this scenario the students would be loading one container and the teachers would be loading two.

Container services

When you load a container, the Fluid service will also return a service-specific services object. This object contains references to useful services you can use to build richer apps. An example of a container service is the Audience, which provides user information for clients that are connected to the container.