Skip to main content
Version: 3.0.0-beta

Sample PRO

Let's go through the code generated by the FireCMS CLI after a PRO project is created. FireCMS is at its core a React library, so the generated code is a React application. The code is structured in a way that you can easily understand and modify it to fit your needs.

Firebase Setup

The first step involves initializing Firebase using the provided configuration. This is necessary for all Firebase-related operations throughout the app.

You can find the firebaseConfig after creating a new webapp in the Firebase console.

const {
firebaseApp,
firebaseConfigLoading,
configError
} = useInitialiseFirebase({
firebaseConfig
});

This snippet sets up Firebase, checks for loading status, and handles configuration errors.

Data Source and Storage Source

Your users will need to interact with data and files, so you need to set up data and storage sources. FireCMS provides a Firestore delegate and Firebase storage source for these operations.

const firestoreDelegate = useFirestoreDelegate({
firebaseApp
});
const storageSource = useFirebaseStorageSource({
firebaseApp
});

You are free to define your own data source and storage source, but these are the default ones provided by FireCMS. Feel free to reach out to us if you need help setting up your own data source or storage source.

Collection Configuration Plugin

The collection editor plugin allows you to include a UI for editing collection configurations. You can choose where the config is stored, and pass the configuration to the plugin. We include a controller that saves the configuration in your Firestore database. The default path is __FIRECMS/config/collections.

The controller includes a few methods you can use in your own components to manage the collection configuration.

const collectionConfigController = useFirestoreCollectionsConfigController({
firebaseApp
});

You are free to define your collections in code, or use the UI to define them. You can also allow the modification in the UI of the collections defined in code. You can then merge the collections defined in code with the ones defined in the UI.

    const collectionsBuilder = useCallback(() => {
// Here we define a sample collection in code.
const collections = [
productsCollection
// Your collections here
];
// You can merge collections defined in the collection editor (UI) with your own collections
return mergeCollections(collections, collectionConfigController.collections ?? []);
}, [collectionConfigController.collections]);

In order to add the collection editor plugin, you need to include it in the list of plugins passed to the FireCMS component.

    const collectionEditorPlugin = useCollectionEditorPlugin({
collectionConfigController
});

Authorization Management

Managing user authentication and permissions is critical for security and proper access control. FireCMS provides an Authenticator interface that you can implement to define your own authentication logic.

You can validate the user's access to the main view based on their authentication status and permissions.

    const myAuthenticator: Authenticator<FirebaseUserWrapper> = useCallback(async ({
user,
authController
}) => {

if (user?.email?.includes("flanders")) {
// You can throw an error to prevent access
throw Error("Stupid Flanders!");
}

const idTokenResult = await user?.firebaseUser?.getIdTokenResult();
const userIsAdmin = idTokenResult?.claims.admin || user?.email?.endsWith("@firecms.co");

console.log("Allowing access to", user);

// we allow access to every user in this case
return true;
}, []);

const {
authLoading,
canAccessMainView,
notAllowedError
} = useValidateAuthenticator({
authController,
authenticator: myAuthenticator,
dataSourceDelegate: firestoreDelegate,
storageSource
});

(if you use the user management system, you can use the authenticator provided by the UserManagement controller. See below)

User Management

FireCMS PRO includes a user management system that allows you to define roles and permissions for users. The UserManagement interface provides methods to define roles and permissions, as well as a loading state to manage. We include a controller that stores user roles and permissions in Firestore. You are free to define your own user management system.

    const userManagement = useFirestoreUserManagement({
firebaseApp
});

then build the user management plugin and include it in the list of plugins passed to the FireCMS component.

    const userManagementPlugin = useUserManagementPlugin({ userManagement });

You can delegate all the authentication logic to the user management system, by using the authenticator provided by the UserManagement controller.

    const {
authLoading,
canAccessMainView,
notAllowedError
} = useValidateAuthenticator({
authController,
disabled: userManagement.loading,
authenticator: userManagement.authenticator,
dataSourceDelegate: firestoreDelegate,
storageSource
});

The Auth Controller

The AuthController is the controller in charge of managing authentication. It provides methods to sign in, sign out, and get the current user. You can also define roles for users. You can access this controller from within your components using the useAuthController hook.

const authController: FirebaseAuthController = useFirebaseAuthController({
firebaseApp,
signInOptions: ["google.com", "password"], // you can pick many more options
loading: userManagement.loading,
defineRolesFor: userManagement.defineRolesFor
});

In this case we are hooking the AuthController to the UserManagement controller, so we can define roles for users based on the user management system.

Mode Controller & User Config Persistence

Adjusting UI preferences, like theme mode, enhances user experience.

const modeController = useBuildModeController();
const userConfigPersistence = useBuildLocalConfigurationPersistence();

These controllers enable theme mode toggling and local storage of user preferences.

The internal navigation controller manages the app's navigation, leveraging the collections and permissions setup. Here you can define your collections, views, and admin views. You can also pass the authController and dataSourceDelegate to the NavigationController. Optionally, you can define the collection permissions in the UserManagement controller.

    const collectionsBuilder = useCallback(() => {
// Here we define a sample collection in code.
const collections = [
productsCollection
// Your collections here
];
// You can merge collections defined in the collection editor (UI) with your own collections
return mergeCollections(collections, collectionConfigController.collections ?? []);
}, [collectionConfigController.collections]);

// Here you define your custom top-level views
const views: CMSView[] = useMemo(() => ([{
path: "example",
name: "Example CMS view",
view: <ExampleCMSView/>
}]), []);

const navigationController = useBuildNavigationController({
collections: collectionsBuilder,
views,
authController,
dataSourceDelegate: firestoreDelegate,
adminViews: userManagementAdminViews,
collectionPermissions: userManagement.collectionPermissions
});

This controller leverages the built collections and permissions setup to manage navigation efficiently.

Wiring it all up

Once you have all the controllers set up, you can pass them to the FireCMS component, along with the plugins you want to use. The FireCMS component will handle the rest, and render the main view or the login view based on the user's authentication status.

Note how you can customize the main view based on the user's authentication status and permissions. The default login view is a Firebase login view, but you can define your own login view.

The SideDialogs component is used to render the lateral dialogs, like the entity detail view. The NavigationRoutes component is used to render the main navigation routes. It uses react-router-dom to handle the routing, but you are free to replace it with your own routing system.

    return (
<SnackbarProvider>
<ModeControllerProvider value={modeController}>

<FireCMS
navigationController={navigationController}
authController={authController}
userConfigPersistence={userConfigPersistence}
dataSourceDelegate={firestoreDelegate}
storageSource={storageSource}
plugins={[dataEnhancementPlugin, importExportPlugin, userManagementPlugin, collectionEditorPlugin]}
>
{({
context,
loading
}) => {

let component;
if (loading || authLoading) {
component = <CircularProgressCenter size={"large"}/>;
} else {
if (!canAccessMainView) {
component = (
<FirebaseLoginView
allowSkipLogin={false}
signInOptions={signInOptions}
firebaseApp={firebaseApp}
authController={authController}
notAllowedError={notAllowedError}/>
);
} else {
component = (
<Scaffold
name={name}
autoOpenDrawer={false}>
<NavigationRoutes/>
<SideDialogs/>
</Scaffold>
);
}
}

return component;
}}
</FireCMS>
</ModeControllerProvider>
</SnackbarProvider>
);
Sign up to our newsletter to get the latest news and updates. No spam!