Framework Integration
Cognivo components are Web Components — they work in any framework or vanilla HTML. First-class React and Vue adapters provide native DX with full TypeScript support.
Vanilla HTML / Any Framework
Import once, use everywhere. No framework adapter needed.
<script type="module">
import '@cognivo/components';
</script>
<link rel="stylesheet" href="node_modules/@cognivo/tokens/dist/index.css">
<cg-button variant="primary" size="md">Click me</cg-button>
<ai-chat placeholder="Ask anything..."></ai-chat>
<ai-thinking variant="dots" text="Analyzing..."></ai-thinking>React
Type-safe React wrappers with proper event handling and prop mapping.
Installation
pnpm add @cognivo/adapter-react @cognivo/components @cognivo/tokensSetup
// app.tsx or layout.tsx — import tokens CSS once
import '@cognivo/tokens/dist/index.css';Usage
import { CgButton, CgInput, CgCard, AiChat, AiThinking } from '@cognivo/adapter-react';
function Dashboard() {
const [query, setQuery] = useState('');
return (
<div>
<CgCard variant="elevated">
<CgInput
label="Search"
value={query}
onCgInput={(e) => setQuery(e.detail.value)}
/>
<CgButton variant="primary" onCgClick={() => search(query)}>
Search
</CgButton>
</CgCard>
<AiThinking variant="dots" text="Processing..." />
<AiChat placeholder="Ask a question..." />
</div>
);
}Event Handling
React wrappers map Web Component custom events to React-style callbacks. The event prefix is on + PascalCase event name.
| Web Component Event | React Prop | Detail Type |
|---|---|---|
cg-click | onCgClick | MouseEvent |
cg-input | onCgInput | { value: string } |
cg-change | onCgChange | { value: string, error?: boolean } |
ai-message | onAiMessage | { text: string, role: string } |
TypeScript
// All props and events are fully typed
import type { CgButton, CgInput, AiChat } from '@cognivo/adapter-react';
// Example: typed event handler
const handleInput = (e: CustomEvent<{ value: string }>) => {
console.log(e.detail.value);
};Vue
Vue 3 wrappers with v-model support and typed props.
Installation
pnpm add @cognivo/adapter-vue @cognivo/components @cognivo/tokensSetup
// main.ts
import '@cognivo/tokens/dist/index.css';Usage
<script setup lang="ts">
import { CgButton, CgInput, CgCard, AiChat } from '@cognivo/adapter-vue';
import { ref } from 'vue';
const query = ref('');
</script>
<template>
<CgCard variant="elevated">
<CgInput
label="Search"
:value="query"
@cg-input="query = $event.detail.value"
/>
<CgButton variant="primary" @cg-click="search(query)">
Search
</CgButton>
</CgCard>
<AiChat placeholder="Ask a question..." />
</template>Event Handling
Vue wrappers emit the same custom events as the Web Components. Use @event-name syntax.
Svelte, Angular, SolidJS
No adapter needed — use Web Components directly.
Svelte
<script>
import '@cognivo/components';
import '@cognivo/tokens/dist/index.css';
</script>
<cg-button variant="primary" on:cg-click={handleClick}>
Click me
</cg-button>Angular
// app.module.ts — enable custom elements
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// Import in styles.css
@import '@cognivo/tokens';
// Use in templates
// <cg-button variant="primary">Click</cg-button>Dark Mode (All Frameworks)
// Set on document root — works in all frameworks
document.documentElement.setAttribute('data-theme', 'dark');
// React example
function ThemeToggle() {
const [dark, setDark] = useState(true);
useEffect(() => {
document.documentElement.setAttribute(
'data-theme', dark ? 'dark' : 'light'
);
}, [dark]);
return <CgSwitch checked={dark} onCgChange={() => setDark(!dark)} />;
}Tree Shaking
Import individual components to reduce bundle size:
// Full library (registers all 182 components)
import '@cognivo/components';
// Individual imports (tree-shakeable)
import { CgButton } from '@cognivo/components';
import { AiChat } from '@cognivo/components';Server-Side Rendering
Cognivo components SSR cleanly via @lit-labs/ssr. The @cognivo/ssrpackage provides ergonomic helpers that render Lit templates to a string with Declarative Shadow DOM, so the markup is visible on first paint before hydration.
Installation
pnpm add @cognivo/ssr @cognivo/components litCore Usage
import { renderToString, html } from '@cognivo/ssr';
import '@cognivo/components';
const markup = renderToString(html`
<cg-card>
<cg-button variant="primary">Click me</cg-button>
</cg-card>
`);Next.js (App Router)
Render on the server, then import the client bundle to upgrade the custom elements after hydration.
// app/page.tsx — Server Component
import { renderToString, html } from '@cognivo/ssr';
import '@cognivo/components'; // registers element classes for SSR
export default function Page() {
const markup = renderToString(html`
<cg-card>
<cg-button variant="primary">Get started</cg-button>
</cg-card>
`);
return <div dangerouslySetInnerHTML={{ __html: markup }} />;
}// app/cognivo-client.tsx — Client Component
'use client';
import { useEffect } from 'react';
export function CognivoClient() {
useEffect(() => {
// Upgrades the custom elements in the SSR'd markup
import('@cognivo/components');
}, []);
return null;
}Astro
Use set:html with the rendered markup, then ship a client script that registers the elements for hydration.
---
import { renderToString, html } from '@cognivo/ssr';
import '@cognivo/components';
const markup = renderToString(html`
<cg-card>
<cg-button variant="primary">Click me</cg-button>
</cg-card>
`);
---
<div set:html={markup}></div>
<script>
import '@cognivo/components';
</script>Remix
The same pattern works in Remix: call renderToString inside your loader or route module, emit the HTML in the response, and import @cognivo/components in the client entry to upgrade the elements after hydration.
@webcomponents/template-shadowroot polyfill alongside the client bundle.