Este guia fornece tudo o que você precisa para integrar rapidamente a biblioteca react-lgpd-consent em seu projeto React.
💡 Procurando por receitas práticas? Veja RECIPES.md para casos de uso específicos como Next.js App Router, CSP/nonce, Consent Mode v2 e subdomínios.
npm install react-lgpd-consent
# ou
yarn add react-lgpd-consent
npm install @mui/material @mui/icons-material @emotion/react @emotion/styled
ℹ️ Modularização (v0.5.0+)
react-lgpd-consentcontinua sendo o pacote principal publicado.@react-lgpd-consent/coreexpõe apenas contextos, hooks e utilitários (sem UI).@react-lgpd-consent/muipublica os componentes MUI e re-exporta o core.- Para UI-only (bundle menor), use
@react-lgpd-consent/mui/ui.- Subpaths do agregador:
react-lgpd-consent/coreereact-lgpd-consent/mui.
import React from 'react'
import { ConsentProvider } from 'react-lgpd-consent'
function App() {
return (
<ConsentProvider
categories={{
enabledCategories: ['analytics', 'marketing'],
}}
>
<main>
<h1>Minha Aplicação</h1>
{/* Seu conteúdo aqui */}
</main>
</ConsentProvider>
)
Este repositório possui um Storybook interativo para testes manuais e exploração visual dos componentes. Comandos rápidos:
npm run storybook
npm run build-storybook
Notes:
.storybook/preview.tsx) applies a clean environment between stories (removes consent cookie and performs defensive DOM cleanup). Check that file when creating stories that rely on a clean initial state.Os exemplos a seguir integram GTM/GA4 com Consent Mode v2 e garantem que nenhum script de tracking rode antes do consentimento. Eles também mostram como usar ConsentScriptLoader e sincronizar os sinais do Consent Mode via gtag('consent', ...).
examples/next-app-router/*, examples/vite/*npm create next-app@latest my-app --ts --eslint --src-dir --app --no-tailwind --no-experimental-app
cd my-app
npm i react-lgpd-consent @mui/material @mui/icons-material @emotion/react @emotion/styled
.env.localNEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
NEXT_PUBLIC_GTM_ID=GTM-XXXXXXX
examples/next-app-router/app/layout.tsx → app/layout.tsxexamples/next-app-router/app/page.tsx → app/page.tsxexamples/next-app-router/components/ClientConsent.tsx → app/components/ClientConsent.tsxObservação: nos arquivos copiados, troque imports relativos para import { ConsentProvider, ConsentScriptLoader } from 'react-lgpd-consent'.
ClientConsent é um componente client-only (via dynamic(..., { ssr: false }) no layout) que:
dataLayer/gtag e define consent default = denied para todos os sinais (ad_storage, ad_user_data, ad_personalization, analytics_storage).gtag('consent','update', ...) mapeando as categorias: analytics → analytics_storage, marketing → ad_*.ConsentScriptLoader para carregar GTM/GA4 somente quando as categorias permitirem. Antes disso, nenhum script de tracking é carregado.npm run dev
Validação rápida:
gtm.js/gtag/js até aceitar preferências.analytics, o GA4 é carregado; ao aceitar marketing, os sinais ad_* são atualizados como granted.npm create vite@latest my-app -- --template react-ts
cd my-app
npm i react-lgpd-consent @mui/material @mui/icons-material @emotion/react @emotion/styled
.envVITE_GA_ID=G-XXXXXXXXXX
VITE_GTM_ID=GTM-XXXXXXX
examples/vite/index.html → index.html (não adicione scripts do GA/GTM aqui)examples/vite/src/main.tsx → src/main.tsxexamples/vite/src/App.tsx → src/App.tsxexamples/vite/src/consent/GtagConsent.tsx → src/consent/GtagConsent.tsxObservação: nos arquivos copiados, troque imports relativos para import { ... } from 'react-lgpd-consent'.
npm run dev
Validação rápida:
gtag('consent','update', ...) corretamente por categoria.Disponível a partir da v0.4.0.
Fonte única de verdade
categories do ConsentProvider.useConsent, useCategories) e as integrações (ConsentScriptLoader) leem a mesma definição. Não declare categorias em outros lugares.O que é obrigatório?
necessary é obrigatória (e já é sempre incluída automaticamente).analytics, marketing, functional, etc.) são opcionais e dependem do seu caso de negócio. Se você não usa analytics/ads/chat, simplesmente não habilite essas categorias.Como “esconder” categorias que não uso?
enabledCategories e não declará-las em customCategories. A UI não exibirá toggles para categorias ausentes.Exemplo A — Somente necessários (mínimo, comum para apps internos/governo sem tracking)
import { ConsentProvider } from 'react-lgpd-consent'
export default function App() {
return (
<ConsentProvider
categories={{ enabledCategories: [] }}
texts={{ bannerMessage: 'Usamos apenas cookies necessários para funcionamento.' }}
>
<YourApp />
</ConsentProvider>
)
}
Exemplo B — Conjunto completo (site com analytics e marketing)
import { ConsentProvider } from 'react-lgpd-consent'
export default function App() {
return (
<ConsentProvider
categories={{ enabledCategories: ['analytics', 'marketing', 'functional'] }}
>
<YourApp />
</ConsentProvider>
)
}
Boas práticas
categories explicitamente. Em DEV, a biblioteca avisa quando categories não foi configurado para evitar ambiguidades.ConsentScriptLoader e categorias adequadas.Em desenvolvimento, a biblioteca valida a configuração e mostra mensagens amigáveis no console. Nada disso impacta produção (onde só ocorre uma sanitização leve).
Avisos comuns e como corrigir:
Prop 'categories' não fornecida... — defina categories.enabledCategories de forma explícita; exemplo mínimo: categories={{ enabledCategories: [] }}.'necessary' é sempre incluída automaticamente — remova 'necessary' de enabledCategories (ela já é incluída por padrão).IDs de categoria duplicados detectados — revise enabledCategories e customCategories para garantir que não há IDs repetidos.enabledCategories contém valores inválidos — verifique se todos os itens são strings não vazias (IDs de categoria).customCategories: ... — ... deve ser uma string não vazia — preencha id, name e description das categorias customizadas.Notas:
NODE_ENV !== 'production'.'necessary' se vier por engano, mantendo o comportamento seguro.Objetivo: evitar hydration mismatch, hooks em Server Components e vazamento de scripts.
Padrões recomendados
ConsentProvider apenas no cliente.dynamic(() => import('./ClientConsent'), { ssr: false }) no RootLayout (Server Component) e mova hooks e efeitos para o componente cliente.window/document no topo de módulo; use apenas dentro de useEffect.gtag('consent','default', denied) antes de carregar GTM/GA4; depois, atualize sinais na mudança de preferências.Exemplo de RootLayout (Server) + Client wrapper
// app/layout.tsx (Server Component)
import dynamic from 'next/dynamic'
const ClientConsent = dynamic(() => import('./components/ClientConsent'), { ssr: false })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="pt-BR">
<body>
<ClientConsent>{children}</ClientConsent>
</body>
</html>
)
}
// app/components/ClientConsent.tsx (Client Component)
'use client'
import React from 'react'
import { ConsentProvider, ConsentScriptLoader } from 'react-lgpd-consent'
import { COMMON_INTEGRATIONS } from 'react-lgpd-consent'
import { useConsent } from 'react-lgpd-consent'
function BootstrapConsentMode() {
React.useEffect(() => {
const w = window as any
w.dataLayer = w.dataLayer ?? []
w.gtag =
w.gtag ??
((...args: any[]) => {
if (typeof w.dataLayer?.push === 'function') {
w.dataLayer.push(args)
}
})
w.gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
})
}, [])
return null
}
function SyncConsentMode() {
const { consented, preferences } = useConsent()
React.useEffect(() => {
if (!consented) return
const w = window as any
w.gtag?.('consent', 'update', {
analytics_storage: preferences.analytics ? 'granted' : 'denied',
ad_storage: preferences.marketing ? 'granted' : 'denied',
ad_user_data: preferences.marketing ? 'granted' : 'denied',
ad_personalization: preferences.marketing ? 'granted' : 'denied',
})
}, [consented, preferences])
return null
}
export default function ClientConsent({ children }: { children: React.ReactNode }) {
const GA = process.env.NEXT_PUBLIC_GA_ID!
const GTM = process.env.NEXT_PUBLIC_GTM_ID!
return (
<ConsentProvider categories={{ enabledCategories: ['analytics', 'marketing', 'functional'] }} blocking>
<BootstrapConsentMode />
<SyncConsentMode />
<ConsentScriptLoader
integrations={[
COMMON_INTEGRATIONS.googleAnalytics({ measurementId: GA }),
COMMON_INTEGRATIONS.googleTagManager({ containerId: GTM }),
]}
/>
{children}
</ConsentProvider>
)
}
Ordem de provedores e estilos (MUI/Emotion)
CacheProvider (Emotion) ou StyledEngineProvider com injectFirstThemeProvider (MUI)CssBaselineConsentProvider (sem criar tema por padrão)Z-index e Portals
zIndex do tema; modals/portals padrão usam zIndex.modal = 1300.z-index: 1299; o Modal/Banner usa camadas ≥ 1300.theme.zIndex (ex.: appBar: 1200, modal: 1300+) ou os designTokens conforme a necessidade.Bloqueio “hard” (sem foco no app)
blockingMode="hard" quando precisar impedir navegação por teclado fora do banner/modal.inert e aria-hidden no conteúdo da aplicação até a decisão do usuário.Checklist SSR (evite hydration mismatch)
'use client' no topo).window/document/localStorage no topo de módulo (apenas em useEffect).dynamic(..., { ssr: false }) para wrappers que usam hooks e efeitos do consentimento.ConsentScriptLoader).<script> de GTM/GA4 em head/body; todo carregamento vem do loader.No modo bloqueante, o banner usa um backdrop para focar a atenção do usuário. Você pode controlar via design tokens:
<ConsentProvider
categories={{ enabledCategories: ['analytics'] }}
designTokens={{
layout: {
// false: transparente | 'auto': ajusta ao tema | string: cor custom (ex.: '#00000088')
backdrop: 'auto',
},
colors: {
// Se omitido, usa o palette do tema MUI (background.paper, text.primary)
// background: '#1e1e1e',
// text: '#ffffff',
},
}}
>
<App />
</ConsentProvider>
Se colors.background ou colors.text não forem fornecidos, a lib usa automaticamente theme.palette.background.paper e theme.palette.text.primary do MUI, garantindo compatibilidade com dark mode.
Durante o desenvolvimento, o console exibe um guia com:
Adicione categorias específicas do seu projeto (ex.: chat de suporte, players de vídeo, AB testing):
<ConsentProvider
categories={{
enabledCategories: ['analytics'],
customCategories: [
{ id: 'chat', name: 'Chat de Suporte', description: 'Widget de chat' },
{ id: 'video', name: 'Vídeo', description: 'Players incorporados' },
{ id: 'abTesting', name: 'A/B Testing', description: 'Experimentos de interface' },
],
}}
>
<App />
</ConsentProvider>
import { useConsent } from 'react-lgpd-consent'
function MyComponent() {
const { preferences } = useConsent()
// Verificar se o usuário consentiu com categorias específicas
const canShowChat = preferences?.chat === true
const canLoadVideos = preferences?.video === true
const canRunABTests = preferences?.abTesting === true
return (
<div>
{canShowChat && <ChatWidget />}
{canLoadVideos && <VideoPlayer src="..." />}
{canRunABTests && <ABTestVariant />}
</div>
)
}
| Prop | Tipo | Obrigatória | Padrão | Descrição |
|---|---|---|---|---|
categories |
ProjectCategoriesConfig |
✅ Sim | - | Define as categorias de cookies do projeto |
texts |
Partial<AdvancedConsentTexts> |
❌ Não | Textos padrão PT-BR | Customiza textos da interface |
language |
'pt' | 'en' | 'es' | 'fr' | 'de' | 'it' |
❌ Não | 'pt' |
Resolve textos via i18n do Provider |
textVariant |
'formal' | 'casual' | 'concise' | 'detailed' |
❌ Não | - | Aplica variação de tom nos textos |
theme |
any |
❌ Não | Tema padrão | Tema Material-UI para os componentes |
designTokens |
DesignTokens |
❌ Não | Tokens padrão | Tokens de design para customização avançada |
blocking |
boolean |
❌ Não | false |
Exibe overlay bloqueando interação até decisão |
blockingMode |
'soft' | 'hard' |
❌ Não | 'soft' |
Intensidade do bloqueio (hard deixa app inerte) |
blockingStrategy |
'auto' | 'provider' | 'component' |
❌ Não | 'auto' |
Estratégia de renderização do overlay |
hideBranding |
boolean |
❌ Não | false |
Oculta branding "fornecido por" |
onConsentGiven |
(state: ConsentState) => void |
❌ Não | - | Callback na primeira vez que usuário consente |
onPreferencesSaved |
(prefs: ConsentPreferences) => void |
❌ Não | - | Callback quando preferências são salvas |
disableDeveloperGuidance |
boolean |
❌ Não | false |
Desativa orientações no console |
disableFloatingPreferencesButton |
boolean |
❌ Não | false |
Desabilita botão flutuante de preferências |
CookieBannerComponent |
React.ComponentType<CustomCookieBannerProps> |
❌ Não | Banner padrão | Componente de banner customizado |
PreferencesModalComponent |
React.ComponentType<CustomPreferencesModalProps> |
❌ Não | Modal padrão | Componente de modal customizado |
FloatingPreferencesButtonComponent |
React.ComponentType<CustomFloatingPreferencesButtonProps> |
❌ Não | Botão padrão | Componente de botão flutuante customizado |
cookieBannerProps |
object |
❌ Não | {} |
Props adicionais para o banner |
preferencesModalProps |
object |
❌ Não | {} |
Props adicionais para o modal |
floatingPreferencesButtonProps |
object |
❌ Não | {} |
Props adicionais para o botão flutuante |
initialState |
ConsentState |
❌ Não | - | Estado inicial para hidratação SSR |
cookie |
Partial<ConsentCookieOptions> |
❌ Não | Opções padrão | Configurações do cookie (override fino de name, domain, sameSite etc.) |
storage |
ConsentStorageConfig |
❌ Não | { namespace: 'lgpd-consent', version: '1' } |
Namespace, versão e domínio compartilhado da chave de consentimento |
onConsentVersionChange |
(context: ConsentVersionChangeContext) => void |
❌ Não | Reset automático | Hook disparado após bump da chave; use para limpar caches adicionais |
Use cookieBannerProps e floatingPreferencesButtonProps para evitar colisões com footers,
chat widgets ou barras fixas:
<ConsentProvider
categories={{ enabledCategories: ['analytics', 'marketing'] }}
cookieBannerProps={{
position: 'bottom',
anchor: 'center',
offset: 72, // afasta o banner do footer fixo
}}
floatingPreferencesButtonProps={{
position: 'bottom-right',
offset: 96, // evita colisão com o banner/footer
}}
/>
Use language para resolver texts.i18n em runtime sem rebuilds:
import { ConsentProvider, EXPANDED_DEFAULT_TEXTS } from 'react-lgpd-consent'
<ConsentProvider
categories={{ enabledCategories: ['analytics'] }}
texts={EXPANDED_DEFAULT_TEXTS}
language="en"
>
<YourApp />
</ConsentProvider>
storage.namespace e storage.version geram automaticamente o nome do cookie via buildConsentStorageKey, mantendo o schema namespace__v<versão> (lgpd-consent__v1 por padrão).storage.domain centraliza o domínio compartilhado (ex.: .gov.br) para que um único banner sirva múltiplos subdomínios.onConsentVersionChange é chamado sempre que a chave muda. O reset do estado é automático, mas o hook permite limpar caches customizados (ex.: localStorage, indexedDB) antes de liberar a nova experiência.storage.version mudou. O bump NÃO é breaking change porque a API pública permanece compatível—apenas força o fluxo de re-consent.storage continua usando lgpd-consent__v1; ao aumentar a versão apenas ocorre re-consentimento.storage.version força o fluxo completo: cookie antigo removido, ConsentProvider volta ao estado sem consentimento e o usuário vê o banner novamente.storage.domain usa um domínio com ponto (.example.com).onConsentVersionChange entrega previousKey, nextKey e resetConsent para coordenar invalidação de caches externos.necessary é adicionada automaticamente pelo ConsentProvider e sempre persistida como true.setPreference('necessary', false) e setPreferences({ necessary: false, ... }) são ignorados com logs de aviso — o estado permanece com necessary=true.PreferencesModal padrão exibe a categoria com switch desabilitado e o texto Cookies necessários (sempre ativos).writeConsentCookie garante necessary=true mesmo que o estado enviado esteja corrompido.useConsent, useCategoryStatus) e integrações (ConsentScriptLoader, dataLayer) sempre recebem necessary=true.import { ConsentProvider } from '@react-lgpd-consent/core'
export function MinimalBoundary({ children }: { children: React.ReactNode }) {
return <ConsentProvider categories={{ enabledCategories: [] }}>{children}</ConsentProvider>
}
necessary=true em todos os caminhos.import { buildConsentStorageKey, ConsentProvider } from 'react-lgpd-consent'
function ComplianceWrapper({ children }: { children: React.ReactNode }) {
return (
<ConsentProvider
categories={{ enabledCategories: ['analytics', 'marketing'] }}
storage={{
namespace: 'portal.gov.br',
version: '2025-Q4',
domain: '.gov.br',
}}
cookie={{
// Opcional: sobrescreva o nome explicitamente (útil para auditoria legada)
name: buildConsentStorageKey({ namespace: 'portal.gov.br', version: '2025-Q4' }),
}}
onConsentVersionChange={({ previousKey, nextKey, resetConsent }) => {
console.info('[consent] versão atualizada', { previousKey, nextKey })
globalThis.window?.dataLayer?.push({
event: 'consent_version_bumped',
previousKey,
nextKey,
})
localStorage.removeItem('marketing-optins')
resetConsent()
}}
>
{children}
</ConsentProvider>
)
}
import React from 'react'
import { ConsentProvider, type CustomCookieBannerProps } from 'react-lgpd-consent'
// Componente de banner customizado
const MeuBannerCustomizado: React.FC<CustomCookieBannerProps> = ({
consented,
acceptAll,
rejectAll,
openPreferences,
texts,
blocking,
}) => {
return (
<div
style={{
position: 'fixed',
bottom: 0,
left: 0,
right: 0,
backgroundColor: blocking ? 'red' : 'blue',
color: 'white',
padding: '1rem',
zIndex: 1000,
}}
>
<p>{texts.bannerMessage}</p>
<div style={{ display: 'flex', gap: '1rem', marginTop: '1rem' }}>
<button onClick={acceptAll}>{texts.acceptAll}</button>
<button onClick={rejectAll}>{texts.declineAll}</button>
<button onClick={openPreferences}>{texts.preferences}</button>
</div>
</div>
)
}
// Usando o banner customizado
function App() {
return (
<ConsentProvider
categories={{ enabledCategories: ['analytics'] }}
CookieBannerComponent={MeuBannerCustomizado}
blocking={true}
>
<main>Minha App</main>
</ConsentProvider>
)
}
A biblioteca react-lgpd-consent não injeta um ThemeProvider global por conta própria. Ela foi projetada para herdar o tema do app quando um ThemeProvider do MUI está presente. Se você precisa garantir um tema de fallback apenas para os componentes da biblioteca, use a fábrica exportada createDefaultConsentTheme() e passe pelo prop theme do ConsentProvider:
import { ConsentProvider, createDefaultConsentTheme } from 'react-lgpd-consent'
<ConsentProvider
theme={createDefaultConsentTheme()}
categories={{ enabledCategories: ['analytics'] }}
>
<App />
</ConsentProvider>
Isso evita alterações indesejadas no contexto do MUI do seu app e problemas de SSR.
import React from 'react'
import { ConsentProvider, type CustomPreferencesModalProps } from 'react-lgpd-consent'
const MeuModalCustomizado: React.FC<CustomPreferencesModalProps> = ({
preferences,
setPreferences,
closePreferences,
isModalOpen,
texts,
}) => {
if (!isModalOpen) return null
return (
<div
style={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: 'white',
border: '2px solid #ccc',
borderRadius: '8px',
padding: '2rem',
zIndex: 2000,
minWidth: '400px',
}}
>
<h2>{texts.modalTitle}</h2>
<p>{texts.modalIntro}</p>
{/* Lista de categorias */}
<div style={{ margin: '1rem 0' }}>
{Object.entries(preferences).map(([category, enabled]) => (
<label key={category} style={{ display: 'block', marginBottom: '0.5rem' }}>
<input
type="checkbox"
checked={enabled}
onChange={(e) =>
setPreferences({
...preferences,
[category]: e.target.checked,
})
}
disabled={category === 'necessary'}
/>{' '}
{category === 'necessary' ? texts.necessaryAlwaysOn : `Cookies ${category}`}
</label>
))}
</div>
<div style={{ display: 'flex', gap: '1rem', justifyContent: 'flex-end' }}>
<button onClick={closePreferences}>Cancelar</button>
<button onClick={closePreferences}>{texts.save}</button>
</div>
</div>
)
}
function App() {
return (
<ConsentProvider
categories={{ enabledCategories: ['analytics', 'marketing'] }}
PreferencesModalComponent={MeuModalCustomizado}
>
<main>Minha App</main>
</ConsentProvider>
)
}
Para casos mais avançados onde você precisa exibir informações detalhadas sobre cada cookie (nome, finalidade, duração, provedor), use getCookiesInfoForCategory junto com useCategories:
import React from 'react'
import {
ConsentProvider,
useCategories,
getCookiesInfoForCategory,
type CustomPreferencesModalProps,
type CookieDescriptor,
} from 'react-lgpd-consent'
const ModalComDetalhesCookies: React.FC<CustomPreferencesModalProps> = ({
preferences,
setPreferences,
closePreferences,
isModalOpen,
texts,
}) => {
const { allCategories } = useCategories()
if (!isModalOpen) return null
// Simula integrações usadas no projeto (normalmente você teria isso em contexto)
const integracoesUsadas = ['google-analytics', 'google-tag-manager', 'mixpanel']
return (
<div
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 2000,
}}
>
<div
style={{
backgroundColor: 'white',
borderRadius: '12px',
padding: '2rem',
maxWidth: '800px',
maxHeight: '80vh',
overflow: 'auto',
boxShadow: '0 10px 25px rgba(0, 0, 0, 0.25)',
}}
>
<h2 style={{ marginBottom: '1rem', color: '#333' }}>{texts.modalTitle}</h2>
<p style={{ marginBottom: '2rem', color: '#666' }}>{texts.modalIntro}</p>
{/* Lista de categorias com detalhes dos cookies */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
{allCategories.map((categoria) => {
const cookiesDetalhados: CookieDescriptor[] = getCookiesInfoForCategory(
categoria.id as any,
integracoesUsadas,
)
return (
<div
key={categoria.id}
style={{
border: '1px solid #e0e0e0',
borderRadius: '8px',
padding: '1.5rem',
backgroundColor: '#fafafa',
}}
>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
<input
type="checkbox"
id={`categoria-${categoria.id}`}
checked={preferences[categoria.id] || false}
onChange={(e) =>
setPreferences({
...preferences,
[categoria.id]: e.target.checked,
})
}
disabled={categoria.essential}
style={{ marginRight: '0.75rem', transform: 'scale(1.2)' }}
/>
<label
htmlFor={`categoria-${categoria.id}`}
style={{ fontSize: '1.1rem', fontWeight: 'bold', color: '#333' }}
>
{categoria.name}
{categoria.essential && (
<span style={{ fontSize: '0.8rem', color: '#888', marginLeft: '0.5rem' }}>
(sempre ativo)
</span>
)}
</label>
</div>
<p style={{ marginBottom: '1rem', color: '#666', fontSize: '0.95rem' }}>
{categoria.description}
</p>
{/* Lista de cookies desta categoria */}
{cookiesDetalhados.length > 0 && (
<details style={{ marginTop: '1rem' }}>
<summary
style={{
cursor: 'pointer',
fontWeight: '500',
color: '#4f46e5',
marginBottom: '0.5rem',
}}
>
Ver cookies desta categoria ({cookiesDetalhados.length})
</summary>
<div style={{ marginTop: '0.75rem', paddingLeft: '1rem' }}>
{cookiesDetalhados.map((cookie, index) => (
<div
key={`${cookie.name}-${index}`}
style={{
backgroundColor: 'white',
border: '1px solid #e5e5e5',
borderRadius: '6px',
padding: '1rem',
marginBottom: '0.75rem',
}}
>
<h4
style={{ margin: '0 0 0.5rem 0', color: '#333', fontSize: '0.95rem' }}
>
<code
style={{
backgroundColor: '#f3f4f6',
padding: '2px 6px',
borderRadius: '4px',
fontFamily: 'monospace',
}}
>
{cookie.name}
</code>
</h4>
{cookie.purpose && (
<p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
<strong>Finalidade:</strong> {cookie.purpose}
</p>
)}
{cookie.duration && (
<p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
<strong>Duração:</strong> {cookie.duration}
</p>
)}
{cookie.provider && (
<p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
<strong>Provedor:</strong> {cookie.provider}
</p>
)}
</div>
))}
</div>
</details>
)}
{/* Fallback para categorias sem cookies catalogados */}
{cookiesDetalhados.length === 0 &&
categoria.cookies &&
categoria.cookies.length > 0 && (
<div style={{ marginTop: '1rem', fontSize: '0.9rem', color: '#666' }}>
<strong>Padrões de cookies:</strong>{' '}
{categoria.cookies.map((pattern, i) => (
<code
key={i}
style={{
backgroundColor: '#f3f4f6',
padding: '2px 4px',
borderRadius: '3px',
marginRight: '0.5rem',
fontFamily: 'monospace',
fontSize: '0.8rem',
}}
>
{pattern}
</code>
))}
</div>
)}
</div>
)
})}
</div>
{/* Botões de ação */}
<div
style={{
display: 'flex',
gap: '1rem',
justifyContent: 'flex-end',
marginTop: '2rem',
paddingTop: '1rem',
borderTop: '1px solid #e0e0e0',
}}
>
<button
onClick={closePreferences}
style={{
padding: '0.75rem 1.5rem',
backgroundColor: '#6b7280',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '0.95rem',
}}
>
Cancelar
</button>
<button
onClick={closePreferences}
style={{
padding: '0.75rem 1.5rem',
backgroundColor: '#4f46e5',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '0.95rem',
}}
>
{texts.save}
</button>
</div>
</div>
</div>
)
}
function AppComModalAvancado() {
return (
<ConsentProvider
categories={{
enabledCategories: ['analytics', 'marketing', 'functional'],
}}
PreferencesModalComponent={ModalComDetalhesCookies}
// Especifique as integrações para obter informações detalhadas dos cookies
scriptIntegrations={[
{ id: 'google-analytics', config: { measurementId: 'GA_MEASUREMENT_ID' } },
{ id: 'google-tag-manager', config: { containerId: 'GTM-XXXXXXX' } },
{ id: 'mixpanel', config: { token: 'MIXPANEL_TOKEN' } },
]}
>
<main>Minha App com Modal Avançado</main>
</ConsentProvider>
)
}
useCategories(): Hook que retorna informações sobre todas as categorias ativasgetCookiesInfoForCategory(categoryId, integrations): Função que retorna detalhes completos dos cookiesCookieDescriptor: Interface TypeScript com name, purpose, duration, provider<details> expansívelimport React from 'react'
import { useOpenPreferencesModal, useConsent } from 'react-lgpd-consent'
function MeuComponente() {
const openPreferences = useOpenPreferencesModal()
const { preferences, acceptAll, rejectAll } = useConsent()
return (
<div>
<h3>Status atual: {preferences.analytics ? 'Analytics ativo' : 'Analytics inativo'}</h3>
<button onClick={openPreferences}>⚙️ Gerenciar Preferências</button>
<button onClick={acceptAll}>✅ Aceitar Todos</button>
<button onClick={rejectAll}>❌ Recusar Todos</button>
</div>
)
}
<!-- Em templates HTML, emails ou widgets externos -->
<button onclick="globalThis.window?.openPreferencesModal?.()">Configurar Cookies</button>
<script>
// Ou em JavaScript puro
function abrirConfiguracoesCookies() {
if (globalThis.window?.openPreferencesModal) {
globalThis.window.openPreferencesModal()
} else {
console.warn('Sistema de consentimento não carregado')
}
}
// Verificar se função está disponível
if (typeof globalThis.window?.openPreferencesModal === 'function') {
console.log('✅ Sistema de consentimento disponível')
}
</script>
import { setDebugLogging, LogLevel } from 'react-lgpd-consent'
// Ativar todos os logs (desenvolvimento)
setDebugLogging(true, LogLevel.DEBUG)
// Apenas logs importantes (staging)
setDebugLogging(true, LogLevel.INFO)
// Apenas erros (produção)
setDebugLogging(true, LogLevel.ERROR)
// Desativar completamente
setDebugLogging(false)
// Em desenvolvimento, você verá logs como:
// [🍪 LGPD-CONSENT] 🔧 Categorias Ativas (para UI customizada)
// [🍪 LGPD-CONSENT] ℹ️ User accepted all cookies
// [🍪 LGPD-CONSENT] 🐛 Category preference changed: analytics = true
import { useConsent } from 'react-lgpd-consent'
function DebugPanel() {
const { preferences, consented } = useConsent()
// Apenas mostrar em desenvolvimento
if (process.env.NODE_ENV !== 'development') return null
return (
<div
style={{
position: 'fixed',
top: 0,
right: 0,
background: 'rgba(0,0,0,0.8)',
color: 'white',
padding: '1rem',
fontSize: '12px',
fontFamily: 'monospace',
}}
>
<h4>🍪 Debug LGPD</h4>
<p>Consentimento: {consented ? '✅' : '❌'}</p>
<pre>{JSON.stringify(preferences, null, 2)}</pre>
</div>
)
}
import React from 'react'
import { ThemeProvider, createTheme } from '@mui/material/styles'
import CssBaseline from '@mui/material/CssBaseline'
import { ConsentProvider } from 'react-lgpd-consent'
// Seu tema personalizado
const meuTema = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
typography: {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
},
})
// Tema específico para componentes de consentimento (opcional)
const temaConsentimento = createTheme({
...meuTema,
components: {
MuiPaper: {
styleOverrides: {
root: {
borderRadius: 12,
},
},
},
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none',
fontWeight: 600,
},
},
},
},
})
function App() {
return (
<ThemeProvider theme={meuTema}>
<CssBaseline />
<ConsentProvider
categories={{
enabledCategories: ['analytics', 'marketing', 'advertising'],
}}
theme={temaConsentimento} // Tema específico para consentimento
texts={{
bannerMessage: 'Utilizamos cookies para personalizar sua experiência.',
acceptAll: 'Aceitar Todos',
declineAll: 'Apenas Necessários',
preferences: 'Personalizar',
}}
onConsentGiven={(state) => {
console.log('✅ Consentimento dado:', state.preferences)
}}
>
<main>
<h1>Minha Aplicação com Material-UI</h1>
{/* Seus componentes aqui */}
</main>
</ConsentProvider>
</ThemeProvider>
)
}
export default App
import { ConsentProvider, type DesignTokens } from 'react-lgpd-consent'
const meusTokens: DesignTokens = {
colors: {
primary: '#6366f1',
secondary: '#f59e0b',
background: '#ffffff',
text: '#1f2937',
border: '#e5e7eb',
},
layout: {
borderRadius: '12px',
spacing: '1rem',
backdrop: 'rgba(0, 0, 0, 0.6)', // ou false para transparente
},
typography: {
fontFamily: '"Inter", system-ui, sans-serif',
fontSize: '14px',
fontWeight: '500',
},
}
function App() {
return (
<ConsentProvider
categories={{ enabledCategories: ['analytics'] }}
designTokens={meusTokens}
blocking={true}
blockingStrategy="provider"
>
<main>Minha App</main>
</ConsentProvider>
)
}
Você também pode complementar com overrides pontuais via sx/tema MUI:
<ConsentProvider
categories={{ enabledCategories: ['analytics'] }}
cookieBannerProps={{ PaperProps: { sx: { borderRadius: 3 } } }}
preferencesModalProps={{ DialogProps: { sx: { '& .MuiDialog-paper': { borderRadius: 4 } } } }}
/>
import { ConsentProvider } from 'react-lgpd-consent'
function App() {
return (
<ConsentProvider
categories={{ enabledCategories: ['analytics'] }}
cookie={{
name: 'meu_app_consent', // Nome customizado
maxAge: 365 * 24 * 60 * 60, // 1 ano em segundos
domain: '.meudominio.com.br', // Cookie compartilhado entre subdomínios
secure: true, // Apenas HTTPS
sameSite: 'Lax', // Política SameSite
}}
>
<main>Minha App</main>
</ConsentProvider>
)
}
<ConsentProvider
categories={{
enabledCategories: ['analytics', 'marketing', 'advertising'],
}}
texts={{
bannerMessage:
'Usamos cookies para melhorar sua experiência de compra e exibir ofertas personalizadas.',
acceptAll: 'Aceitar e continuar',
declineAll: 'Apenas essenciais',
}}
onConsentGiven={(state) => {
// Inicializar ferramentas baseado no consentimento
if (state.preferences.analytics) {
// gtag('config', 'GA_MEASUREMENT_ID')
}
if (state.preferences.marketing) {
// fbq('init', 'FACEBOOK_PIXEL_ID')
}
}}
>
{/* Sua loja */}
</ConsentProvider>
<ConsentProvider
categories={{
enabledCategories: ['analytics'],
}}
disableFloatingPreferencesButton={false}
hideBranding={true}
>
{/* Seu conteúdo */}
</ConsentProvider>
<ConsentProvider
categories={{
enabledCategories: ['analytics', 'functional'],
}}
blocking={true}
blockingStrategy="provider"
disableDeveloperGuidance={false}
onPreferencesSaved={(prefs) => {
// Log de auditoria
console.log('Audit: User preferences updated', prefs)
}}
>
{/* Sua app corporativa */}
</ConsentProvider>
💡 Diagnósticos automáticos: A partir da v0.5.4, o react-lgpd-consent detecta automaticamente problemas comuns (múltiplas instâncias de React, versões incompatíveis) e exibe mensagens detalhadas no console em modo desenvolvimento.
Para problemas mais complexos, consulte o Guia Completo de Troubleshooting.
Se você vê este erro, provavelmente tem múltiplas versões de React carregadas. O diagnóstico automático exibirá instruções específicas para o seu gerenciador de pacotes.
Solução rápida (pnpm):
{
"pnpm": {
"overrides": {
"react": "$react",
"react-dom": "$react-dom"
}
}
}
Mais detalhes: TROUBLESHOOTING.md - Invalid hook call
// ❌ Errado - hook usado fora do provider
function MeuComponente() {
const { preferences } = useConsent() // Erro!
return <div>...</div>
}
function App() {
return (
<div>
<MeuComponente /> {/* useConsent usado aqui falhará */}
<ConsentProvider categories={{ enabledCategories: ['analytics'] }}>
<main>App</main>
</ConsentProvider>
</div>
)
}
// ✅ Correto - hook usado dentro do provider
function App() {
return (
<ConsentProvider categories={{ enabledCategories: ['analytics'] }}>
<div>
<MeuComponente /> {/* Agora funciona */}
<main>App</main>
</div>
</ConsentProvider>
)
}
// Limpar cookie para teste
document.cookie = 'cookieConsent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'
hideBranding e outras configs estão corretasz-index não está sendo sobrescrito por outros elementos// Se você tiver problemas com tipos, adicione ao tsconfig.json:
{
"compilerOptions": {
"moduleResolution": "bundler", // ou "node"
"skipLibCheck": true
}
}
💡 Dica: Use setDebugLogging(true, LogLevel.DEBUG) durante o desenvolvimento para ver logs detalhados do comportamento da biblioteca.