Skip to main content
Version: 3.0.0-beta

Top level views

If you need to develop a custom view that does not map directly to a datasource collection you can implement it as a React component. This is useful for implementing custom dashboards, data visualizations, or any other custom functionality you need.

The top level views can be customized based on the logged in user, so you can implement custom views for different user roles, or even hide them completely for unauthenticated users.

You can use all the components and hooks provided by FireCMS. Check the components gallery for more information.

You can also use all the hooks, including authentication, navigation, datasource (Firestore), storage, etc.

You can also include collection views inside your custom views, or use the side panel to see entity details, or use with completely custom components.

You need to define the name, route and the component, and add it to the main navigation, as the example below.

By default, it will show in the main navigation view.

For custom views you can define the following props:

  • path string

CMS Path you can reach this view from. If you include multiple paths, only the first one will be included in the main menu

  • name: string

Name of this view

  • description?: string

Optional description of this view. You can use Markdown

  • hideFromNavigation?: boolean

Should this view be hidden from the main navigation panel. It will still be accessible if you reach the specified path

  • view: React.ReactNode

Component to be rendered. This can be any React component, and can use any of the provided hooks

  • group?: string

Optional field used to group top level navigation entries under a navigation view.

Example self-hosted FireCMS

For self-hosted FireCMS, you can define your custom top level views by adding them in your useBuildNavigationController component. You can pass them as an array of CMSView objects or a callback function that returns an array of CMSView objects. In the callback, you can use the user object to conditionally show or hide views based on the user role, as well as access the authentication and navigation hooks.

import {CMSView, useBuildNavigationController} from "@firecms/core";
import {useMemo} from "react";

// rest of you main app code

const customViews: CMSView[] = useMemo([{
path: "additional",
name: "Additional view",
description: "This is an example of an additional view that is defined by the user",
// This can be any React component
view: <ExampleCMSView/>
}], []);

const navigationController = useBuildNavigationController({
views: ({ user }) =>{
if(!user) {
return [];
}
return customViews;
},
collections,
authController,
dataSourceDelegate: firestoreDelegate
});

Example FireCMS Cloud

In FireCMS Cloud, you can define your custom top level views by adding them to your FireCMSAppConfig. You can use a callback or add them directly to the views property. The callback includes the logged in user, so you can use it to conditionally show or hide views based on the user role.

import { buildCollection, CMSView } from "@firecms/core";
import { ExampleCMSView } from "./ExampleCMSView";
import { FireCMSAppConfig, FireCMSCloudApp } from "@firecms/cloud";

const projectId = "YOUR_PROJECT_ID";

const customViews: CMSView[] = [{
path: "additional",
name: "Additional view",
description: "This is an example of an additional view that is defined by the user",
// This can be any React component
view: <ExampleCMSView/>
}];

const productCollection = buildCollection<any>({
name: "Product",
id: "products",
path: "products",
properties: {
name: {
name: "Name",
validation: { required: true },
dataType: "string"
}
}
});

const appConfig: FireCMSAppConfig = {
version: "1",
collections: ({ user }) => [
productCollection
],
views: ({ user }) => customViews
};

export default function App() {

return <FireCMSCloudApp
projectId={projectId}
appConfig={appConfig}
/>;
}

Example Additional view

Your custom view is implemented as any regular React component. You can use any of the provided hooks, including authentication, navigation, datasource, storage, app stage, etc.

import React from "react";

import {
buildCollection,
Entity,
EntityCollectionView,
useAuthController,
useReferenceDialog,
useSelectionController,
useSideEntityController,
useSnackbarController
} from "@firecms/core";
import { Button, GitHubIcon, IconButton, Paper, Tooltip, Typography, } from "@firecms/ui";

const usersCollection = buildCollection({
path: "users",
id: "users",
name: "Users",
singularName: "User",
group: "Main",
description: "Registered users",
textSearchEnabled: true,
icon: "Person",
properties: {
first_name: {
name: "First name",
dataType: "string"
},
last_name: {
name: "Last name",
dataType: "string"
},
email: {
name: "Email",
dataType: "string",
email: true
},
phone: {
name: "Phone",
dataType: "string"
},
liked_products: {
dataType: "array",
name: "Liked products",
description: "Products this user has liked",
of: {
dataType: "reference",
path: "products"
}
},
picture: {
name: "Picture",
dataType: "map",
properties: {
large: {
name: "Large",
dataType: "string",
url: "image"
},
thumbnail: {
name: "Thumbnail",
dataType: "string",
url: "image"
}
},
previewProperties: ["large"]
}
},
additionalFields: [
{
key: "sample_additional",
name: "Sample additional",
Builder: ({ entity }) => <>{`Generated column: ${entity.values.first_name}`}</>,
dependencies: ["first_name"]
}
]
});

/**
* Sample CMS view not bound to a collection, customizable by the developer

*/
export function ExampleCMSView() {

// hook to display custom snackbars
const snackbarController = useSnackbarController();

const selectionController = useSelectionController();

console.log("Selection from ExampleCMSView", selectionController.selectedEntities);

// hook to open the side dialog that shows the entity forms
const sideEntityController = useSideEntityController();

// hook to do operations related to authentication
const authController = useAuthController();

// hook to open a reference dialog
const referenceDialog = useReferenceDialog({
path: "products",
onSingleEntitySelected(entity: Entity<any> | null) {
snackbarController.open({
type: "success",
message: "Selected " + entity?.values.name
})
}
});

const customProductCollection = buildCollection({
id: "custom_product",
path: "custom_product",
name: "Custom products",
properties: {
name: {
name: "Name",
validation: { required: true },
dataType: "string"
},
very_custom_field: {
name: "Very custom field",
dataType: "string"
}
}
});

const githubLink = (
<Tooltip
asChild={true}
title="Get the source code of this example view">
<IconButton
href={"https://github.com/firecmsco/firecms/blob/main/examples/example_cloud/src/views/ExampleCMSView.tsx"}
rel="noopener noreferrer"
target="_blank"
component={"a"}
size="large">
<GitHubIcon/>
</IconButton>
</Tooltip>
);

return (
<div className="flex h-full">
<div className="m-auto flex flex-col items-center max-w-4xl">

<div className="flex flex-col gap-12 items-start">

<div className="mt-24">
<Typography variant="h4">
This is an example of an additional view
</Typography>
<p>
{authController.user
? <>Logged in as {authController.user.displayName}</>
: <>You are not logged in</>}
</p>
</div>

<div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
<Paper className={"w-full flex flex-col p-4 items-start"}>
<p className="mb-4 flex-grow">
Use this button to select an entity under the path `products` programmatically
</p>
<Button
variant={"outlined"}
size={"small"}
onClick={referenceDialog.open}>
Test reference dialog
</Button>
</Paper>

<Paper className="w-full flex flex-col p-4 items-start">
<p className="mb-4 flex-grow">
Use this button to open a snackbar
</p>
<Button
variant={"outlined"}
size={"small"}
onClick={() => snackbarController.open({
type: "success",
message: "This is pretty cool"
})}>
Test snackbar
</Button>
</Paper>

<Paper className="w-full flex flex-col p-4 items-start">
<p className="mb-4 flex-grow">
Use this button to open an entity in a custom path with a custom schema
</p>
<Button
size={"small"}
variant={"outlined"}
onClick={() => sideEntityController.open({
entityId: "B003WT1622",
path: "/products-test",
collection: customProductCollection,
width: 1000
})}>
Open custom entity
</Button>
</Paper>
</div>

<div className="w-full">
<p className="mb-4">
You can include full entity collections in your views:
</p>
<Paper
className={"h-[400px]"}>
<EntityCollectionView {...usersCollection}
fullPath={"users"}
selectionController={selectionController}/>
</Paper>
</div>

<div className="mt-auto">
{githubLink}
</div>

</div>
</div>
</div>
);
}
Sign up to our newsletter to get the latest news and updates. No spam!