Pular para o conteúdo

Quickstart + Frontend Next.JS

next_js_frontend.png

Você pode verificar uma demo deste template:

Você pode alterar os dados na demo e ver as atualizações, mas ele é redefinido a cada hora.

Obtenha um template frontend com views CRUD de exemplo que incluem:

  • Integração com Firebase e FireCMS. Reutilize componentes tanto no frontend quanto no painel de administração.
  • Prévia ao vivo: veja como as mudanças no CMS serão refletidas no site, usando o mesmo código exato.
  • UI implementada com tailwindcss e componentes Radix UI.
  • Opções de filtragem avançadas
  • Busca de dados ao rolar.
  • Armazenando o estado de filtro na URL.

Este template é extremamente fácil de personalizar para suas necessidades.

A maneira mais fácil de usar o FireCMS com o Next.js é usar o template starter FireCMS PRO. Este template inclui um projeto Next.js com o FireCMS já configurado.

Você pode criar um novo projeto usando o template FireCMS PRO executando:

npx create-firecms-app

ou

yarn create firecms-app

e selecionar o template FireCMS PRO with Next.js frontend.

Em seguida, siga as instruções na tela para criar seu projeto.

O código que será gerado para você é um projeto Next.JS dividido em 3 partes:

  • Uma instância do FireCMS para gerenciar seus dados.
  • Um app frontend que implementa funcionalidade CRUD para uma coleção de produtos, bem como uma view de blog.
  • Uma pasta common com componentes compartilhados.

Você pode usar o FireCMS com o Next.js. O FireCMS é uma biblioteca React, então você pode usá-lo com qualquer framework React.

No caso do Next.js, você está restrito a executar o FireCMS no lado do cliente, pois o Next.js não suporta renderização do lado do servidor de alguns componentes React usados pelo FireCMS.

Vamos criar um app usando FireCMS e Next.js, com o roteador de app configurado para delegar todas as rotas começando com /cms para o FireCMS.

Comece criando seu projeto Next.js:

npx create-next-app@latest

Selecione:

  • TypeScript como idioma
  • ESLint como linter
  • Tailwind CSS como framework CSS
  • src como diretório raiz
  • Sim para o prompt do roteador de app
  • Sim para personalizar o alias de importação padrão (opcional)

Em seguida, vamos instalar o FireCMS PRO. Note que não adicionaremos todos os plugins como o editor de coleções ou aprimoramento de dados, mas você pode adicioná-los conforme necessário.

Instale o FireCMS e suas dependências:

yarn add firebase@^10 @firecms/core@^3.0.0-beta @firecms/firebase@^3.0.0-beta react-router@^6 react-router-dom@^6 @tailwindcss/typography typeface-rubik @fontsource/jetbrains-mono

Agora vamos importar a configuração tailwind do FireCMS. Adicione o preset FireCMS tailwind.config.js, bem como os caminhos de conteúdo para o código-fonte do FireCMS, para que as classes tailwind corretas sejam selecionadas.

import fireCMSConfig from "@firecms/ui/tailwind.config.js";
import type { Config } from "tailwindcss";
const config: Config = {
presets: [fireCMSConfig],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/cms/**/*.{js,ts,jsx,tsx,mdx}",
"./node_modules/@firecms/**/*.{js,ts,jsx,tsx}"
]
};
export default config;

Preferimos desabilitar o yarn pnp para este projeto. Você pode fazer isso criando o arquivo .yarnrc na raiz do seu projeto com o seguinte conteúdo:

nodeLinker: node-modules

O Next.js usa um roteador baseado em arquivos. Neste guia, criaremos o app FireCMS na rota /cms, mas você pode personalizar isso para suas necessidades.

O FireCMS usa react-router, então precisamos configurar o Next.js para delegar todas as rotas começando com /cms para o FireCMS.

Na nossa pasta app, criamos uma pasta chamada cms e dentro dela outra chamada [[...path]]. Isso corresponderá a qualquer rota começando com /cms.

Em seguida, crie o arquivo cms/[[...path]]/page.tsx com o seguinte conteúdo:

Se você não estiver executando o FireCMS no caminho raiz do seu app, you need to set the basePath prop to the path where you are running it. In this case, we are running it in /cms.

"use client";
import { FireCMSApp } from "@/cms/FireCMSApp";
import { FireCMSRouter } from "@firecms/core";
export default function CMS() {
return <FireCMSRouter basePath={"/cms"}>
<FireCMSApp/>
</FireCMSRouter>;
}

Agora vamos criar os componentes FireCMS. Crie o arquivo ./src/cms/FireCMSApp.tsx com o seguinte conteúdo. Lembre-se de substituir o firebaseConfig pela sua própria configuração Firebase.

"use client";
import React, { useCallback } from "react";
import "./index.css";
import "typeface-rubik";
import "@fontsource/jetbrains-mono";
import {
AppBar,
buildCollection,
CircularProgressCenter,
Drawer,
FireCMS,
ModeControllerProvider,
NavigationRoutes,
Scaffold,
SideDialogs,
SnackbarProvider,
useBuildLocalConfigurationPersistence,
useBuildModeController,
useBuildNavigationController,
useValidateAuthenticator
} from "@firecms/core";
import {
FirebaseAuthController,
FirebaseLoginView,
FirebaseSignInProvider,
useFirebaseAuthController,
useFirebaseStorageSource,
useFirestoreDelegate,
useInitialiseFirebase,
} from "@firecms/firebase";
import { useImportPlugin } from "@firecms/data_import";
import { useExportPlugin } from "@firecms/data_export";
import { useBuildUserManagement, userManagementAdminViews, useUserManagementPlugin } from "@firecms/user_management";
import { useFirestoreCollectionsConfigController } from "@firecms/collection_editor_firebase";
import { mergeCollections, useCollectionEditorPlugin } from "@firecms/collection_editor";
//TODO: replace with your own Firebase config
export const firebaseConfig = {
//...
};
const categories = {
fiction: "Fiction",
drama: "Drama",
"fantasy-fiction": "Fantasy fiction",
history: "History",
religion: "Religion",
"self-help": "Self-Help",
"comics-graphic-novels": "Comics & Graphic Novels",
"juvenile-fiction": "Juvenile Fiction",
philosophy: "Philosophy",
fantasy: "Fantasy",
education: "Education",
science: "Science",
medical: "Medical",
cooking: "Cooking",
travel: "Travel"
};
const booksCollection = buildCollection({
name: "Books",
singularName: "Book",
id: "books",
path: "books",
icon: "MenuBook",
group: "Content",
textSearchEnabled: true,
description: "Example of a books collection that allows data enhancement through the use of the **OpenAI plugin**",
properties: {
title: {
name: "Title",
validation: { required: true },
dataType: "string"
},
authors: {
name: "Authors",
dataType: "string"
},
description: {
name: "Description",
dataType: "string",
multiline: true
},
spanish_description: {
name: "Spanish description",
dataType: "string",
multiline: true
},
thumbnail: {
name: "Thumbnail",
dataType: "string",
url: "image"
},
category: {
name: "Category",
dataType: "string",
enumValues: categories
},
tags: {
name: "Tags",
dataType: "array",
of: {
dataType: "string"
}
},
published_year: {
name: "Published Year",
dataType: "number",
validation: {
integer: true,
min: 0
}
},
num_pages: {
name: "Num pages",
dataType: "number"
},
created_at: {
name: "Created at",
dataType: "date",
autoValue: "on_create"
}
}
});
export function FireCMSApp() {
const {
firebaseApp,
firebaseConfigLoading,
configError
} = useInitialiseFirebase({
firebaseConfig
});
// Controller used to manage the dark or light color mode
const modeController = useBuildModeController();
const signInOptions: FirebaseSignInProvider[] = ["google.com"];
// Controller for saving some user preferences locally.
const userConfigPersistence = useBuildLocalConfigurationPersistence();
// Delegate used for fetching and saving data in Firestore
const firestoreDelegate = useFirestoreDelegate({
firebaseApp
});
// Controller used for saving and fetching files in storage
const storageSource = useFirebaseStorageSource({
firebaseApp
});
const collectionConfigController = useFirestoreCollectionsConfigController({
firebaseApp
});
// controller in charge of user management
const userManagement = useBuildUserManagement({
dataSourceDelegate: firestoreDelegate,
});
// Controller for managing authentication
const authController: FirebaseAuthController = useFirebaseAuthController({
firebaseApp,
signInOptions,
loading: userManagement.loading,
defineRolesFor: userManagement.defineRolesFor
});
const {
authLoading,
canAccessMainView,
notAllowedError
} = useValidateAuthenticator({
disabled: userManagement.loading,
authenticator: userManagement.authenticator,
authController,
// authenticator: myAuthenticator,
dataSourceDelegate: firestoreDelegate,
storageSource
});
const collectionsBuilder = useCallback(() => {
const collections = [
booksCollection,
// Your collections here
];
return mergeCollections(collections, collectionConfigController.collections ?? []);
}, [collectionConfigController.collections]);
const navigationController = useBuildNavigationController({
basePath: "/",
collections: collectionsBuilder,
collectionPermissions: userManagement.collectionPermissions,
adminViews: userManagementAdminViews,
authController,
dataSourceDelegate: firestoreDelegate
});
const userManagementPlugin = useUserManagementPlugin({ userManagement });
const importPlugin = useImportPlugin();
const exportPlugin = useExportPlugin();
const collectionEditorPlugin = useCollectionEditorPlugin({
collectionConfigController
});
if (firebaseConfigLoading || !firebaseApp) {
return <><CircularProgressCenter/></>;
}
if (configError) {
return <>{configError}</>;
}
return (
<SnackbarProvider>
<ModeControllerProvider value={modeController}>
<FireCMS
navigationController={navigationController}
authController={authController}
userConfigPersistence={userConfigPersistence}
dataSourceDelegate={firestoreDelegate}
storageSource={storageSource}
plugins={[importPlugin, exportPlugin, userManagementPlugin, collectionEditorPlugin]}
>
{({
context,
loading
}) => {
if (loading || authLoading) {
return <CircularProgressCenter size={"large"}/>;
}
if (!canAccessMainView) {
return <FirebaseLoginView authController={authController}
firebaseApp={firebaseApp}
signInOptions={signInOptions}
notAllowedError={notAllowedError}/>;
}
return <Scaffold
autoOpenDrawer={false}>
<AppBar title={"My demo app"}/>
<Drawer/>
<NavigationRoutes/>
<SideDialogs/>
</Scaffold>;
}}
</FireCMS>
</ModeControllerProvider>
</SnackbarProvider>
);
}

Crie um arquivo chamado index.css na pasta ./src/cms com o seguinte conteúdo:

@import "@firecms/ui/index.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--color-primary: #0070F4;
--color-secondary: #FF5B79;
}
a {
@apply text-blue-600 dark:text-blue-400 dark:hover:text-blue-600 hover:text-blue-800
}

Em seguida, simplesmente execute:

yarn dev

e navegue até http://localhost:3000/cms para ver seu app FireCMS em execução.

  • As imagens são carregadas de forma diferente no Next.js. Você obtém um StaticImageData em vez da URL da imagem (como no vite). Você pode usá-lo em componentes FireCMS que esperam uma URL, usando a propriedade src:
import logo from "./logo.png";
<FirebaseLoginView
logo={logo.src}/>