Skip to main content
Version: 2.0.0-beta

Dynamic root collections

note

In this tutorial we assume you have set up a Firebase project and a FireCMS instance. If you don't, check the Quickstart and Firebase setup sections.

Let's build a more complex example where the main navigation is loaded dynamically from the database. We will use the units collection as the one for generating the rest of the navigation.

For this example we will have Units and Lessons as the main content types, imagine we are modeling the structure for a course.

In the units collection we will create a document for each unit:

dynamic_navigation_collection

And each of those documents will generate a new navigation item. In this case we will have 3 navigation items, one for each unit:

dynamic_navigation_home

Declare the main collection

Let's define the units collection as the main one:

note

We are going to implement a couple on callbacks on entity save and delete to update the navigation when data is changed. That prevents the suer from refreshing the app in order to see the changes. How cool is that?

import { buildCollection } from "firecms";

export type Unit = {
name: string;
description: string;
}

export const unitsCollection = buildCollection<Unit>({
name: "Units",
singularName: "Unit",
group: "Main",
path: "units",
customId: true,
icon: "LocalLibrary",
callbacks: {
onSaveSuccess: ({ context }) => {
context.navigation.refreshNavigation();
},
onDelete: ({ context }) => {
context.navigation.refreshNavigation();
}
},
properties: {
name: {
name: "Name",
validation: { required: true },
dataType: "string"
},
description: {
name: "Description",
validation: { required: true },
dataType: "string",
multiline: true
}
}
});

Dynamic collection builder

Typically in FireCMS you pass a static list of collections to the main CMS component, but in this case we need to build the collections dynamically based on the data in the database.

FireCMS allows you to pass a function that returns a list of collections to the collections prop of the FirebaseCMSApp component.

import { buildCollection, EntityCollectionsBuilder } from "firecms";
import { Unit, unitsCollection } from "./unit_collection";

const collectionBuilder: EntityCollectionsBuilder = async ({ dataSource }) => {
const units = await dataSource.fetchCollection<Unit>({
path: "units",
collection: unitsCollection
});
const lessonCollections = units.map(unit => buildCollection({
name: unit.values.name,
path: `units/${unit.id}/lessons`,
description: unit.values.description,
group: "Units",
properties: {
name: {
name: "Name",
dataType: "string"
}
}
}));

return [
unitsCollection,
...lessonCollections
]
};
tip

Collections can be conveniently loaded asynchronously.

This code is fetching the data that is being generated in the units collection and creating a new collection for each of the documents.

Full code

Wiring it all together we get a simple app that allows us to create new units and lessons and navigate between them:

import React from "react";
import {
buildCollection,
EntityCollectionsBuilder,
FirebaseCMSApp
} from "firecms";

import "typeface-rubik";
import "@fontsource/ibm-plex-mono";

// TODO: Replace with your config
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};

type Unit = {
name: string;
description: string;
}

const unitsCollection = buildCollection<Unit>({
name: "Units",
singularName: "Unit",
group: "Main",
path: "units",
customId: true,
icon: "LocalLibrary",
callbacks: {
onSaveSuccess: ({ context }) => {
context.navigation.refreshNavigation();
},
onDelete: ({ context }) => {
context.navigation.refreshNavigation();
}
},
properties: {
name: {
name: "Name",
validation: { required: true },
dataType: "string"
},
description: {
name: "Description",
validation: { required: true },
dataType: "string",
multiline: true
}
}
});

export default function App() {

const collectionBuilder: EntityCollectionsBuilder = async ({ dataSource }) => {
const units = await dataSource.fetchCollection<Unit>({
path: "units",
collection: unitsCollection
});
const lessonCollections = units.map(unit => buildCollection({
name: unit.values.name,
path: `units/${unit.id}/lessons`,
description: unit.values.description,
group: "Units",
properties: {
name: {
name: "Name",
dataType: "string"
}
}
}));

return [
unitsCollection,
...lessonCollections
]
};

return <FirebaseCMSApp
name={"My learning app"}
collections={collectionBuilder}
firebaseConfig={firebaseConfig}
/>;
}

Need a hand?

Learn how to get the most out of your project with the help of our consulting team.
Get in touch
Sign up to our newsletter to get the latest news and updates. No spam!