What Programming Languages and Frameworks Power AFFiNE's Frontend?

AFFiNE's frontend is built with TypeScript and React 19, utilizing Radix UI primitives, Blocksuite for document editing, Jotai for state management, and a hybrid styling approach using Emotion and Vanilla-Extract.

AFFiNE is an open-source knowledge management platform that unifies documents, whiteboards, and databases in a collaborative workspace. Understanding the programming languages and frameworks used in AFFiNE's frontend reveals how the application delivers high-performance, real-time collaborative editing across web and desktop platforms.

Core Language and UI Foundation

TypeScript as the Primary Language

The entire frontend codebase is written in TypeScript (with TSX for React components), providing static type safety and excellent IDE support. This choice enables robust refactoring across the monorepo structure and ensures type consistency between the React UI layer and the underlying data models.

React 19 for Component Rendering

At the heart of the UI lies React 19 (specifically version 19.2.1 as defined in packages/frontend/core/package.json). The application leverages React's concurrent features and modern hooks architecture to manage complex rendering scenarios, including real-time collaborative cursors and large document trees.

Component Architecture and Design System

Radix UI for Accessible Primitives

AFFiNE builds its component layer on Radix UI primitives (@radix-ui/react-dialog, @radix-ui/react-collapsible, etc.). These unstyled, accessible components provide the behavioral foundation for custom UI elements like modals, dropdown menus, and toolbars without imposing specific visual designs.

Blocksuite for Document and Canvas Editing

The rich-text editing and whiteboard capabilities come from Blocksuite, AFFiNE's custom framework (@blocksuite/affine, @blocksuite/std, @blocksuite/icons). Blocksuite implements a CRDT-based document model that powers the collaborative editor, draggable canvas elements, and block-based content structure. Integration points reside in packages/frontend/core/src/modules/**/view/**/*.tsx.

State Management and Reactivity

Jotai for Global State

Global application state (workspaces, user preferences, navigation history) is managed using Jotai atoms (jotai, jotai-scope). This atomic approach allows fine-grained subscription to state changes, preventing unnecessary re-renders in complex UI trees. State definitions are centralized in packages/frontend/core/src/store/**.

Preact Signals for High-Frequency Updates

For performance-critical scenarios requiring ultra-fast reactivity (drag-and-drop coordinates, animation states, real-time cursor positions), AFFiNE incorporates Preact Signals (@preact/signals-core). Signals provide synchronous, fine-grained updates that bypass React's render cycle when necessary.

Example implementation combining both approaches:

import { atom, useAtom } from 'jotai';
import { signal } from '@preact/signals-core';

const countAtom = atom(0);
const countSignal = signal(0);

export const Counter = () => {
  const [count, setCount] = useAtom(countAtom);
  const inc = () => {
    setCount(c => c + 1);
    countSignal.value += 1;
  };
  return <button onClick={inc}>Clicked {count} ({countSignal.value}) times</button>;
};

Styling Architecture

AFFiNE employs a hybrid styling strategy that combines multiple CSS-in-JS solutions to balance developer experience and runtime performance:

  • Emotion (@emotion/react): Used for dynamic, theme-dependent styles that require JavaScript runtime evaluation. Powers component-specific styling that reacts to state changes.
  • Vanilla-Extract (@vanilla-extract/*): Provides zero-runtime, type-safe CSS extraction. Used for static design tokens and component styles that don't require dynamic computation.
  • TailwindCSS: Utilized specifically for the admin UI panels, offering utility-first rapid development for internal tools.

Theme configuration files reside in packages/frontend/core/src/styles/**.

Routing and Theming

React Router 6 for Navigation

Client-side routing is handled by React Router 6 (react-router-dom v6.30.3), configured in packages/frontend/apps/web/src/router.tsx. The router manages deep linking to specific documents, workspace switching, and modal routing.

next-themes for Color Schemes

Light and dark mode support is implemented using next-themes (next-themes), integrated in packages/frontend/apps/web/src/theme-toggle.tsx. The theme provider manages system preference detection and manual theme toggling.

import { useTheme } from 'next-themes';
import { useEffect } from 'react';

export const ThemeToggle = () => {
  const { setTheme, resolvedTheme } = useTheme();
  useEffect(() => {
    document.body.dataset.theme = resolvedTheme;
  }, [resolvedTheme]);
  return <button onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}>Toggle</button>;
};

Key Implementation Files

The following files illustrate how these frameworks integrate in the AFFiNE codebase:

Area File Path Purpose
Dependencies packages/frontend/core/package.json Declares React 19, Radix UI, Blocksuite, Jotai, and styling libraries
Bootstrap packages/frontend/core/src/bootstrap/index.ts React application entry point and provider composition
Routing packages/frontend/apps/web/src/router.tsx React Router 6 configuration
Components packages/frontend/component/src/** Reusable UI primitives built on Radix
Editor Integration packages/frontend/core/src/modules/**/view/**/*.tsx Blocksuite editor and canvas components
State packages/frontend/core/src/store/** Jotai atoms and signal definitions
Styling packages/frontend/core/src/styles/** Emotion themes and Vanilla-Extract style files
Electron packages/frontend/electron-api/src/** Desktop-specific API bridge

Summary

  • AFFiNE's frontend is built primarily with TypeScript and React 19, leveraging modern concurrent features for high-performance UI rendering.
  • The component architecture combines Radix UI primitives for accessibility with Blocksuite for rich-text and canvas editing capabilities.
  • State management uses a dual approach: Jotai for global application state and Preact Signals for high-frequency reactive updates.
  • Styling employs a hybrid strategy with Emotion for dynamic styles, Vanilla-Extract for zero-runtime CSS, and TailwindCSS for admin interfaces.
  • The application uses React Router 6 for navigation and next-themes for theme management, with specific implementations found in packages/frontend/core/package.json and related source files.

Frequently Asked Questions

Is AFFiNE's frontend built with Next.js?

No, AFFiNE does not use Next.js as its primary framework. While the project includes next-themes for theme management, the core frontend is a React 19 single-page application built with React Router 6 for client-side routing, as seen in packages/frontend/apps/web/src/router.tsx.

What is Blocksuite and how does it relate to AFFiNE?

Blocksuite is AFFiNE's custom rich-text and canvas editing framework (@blocksuite/affine). It provides the CRDT-based document model, block-based content structure, and collaborative editing capabilities that power AFFiNE's note-taking and whiteboard features. The integration code resides in packages/frontend/core/src/modules/**/view/**/*.tsx.

Why does AFFiNE use both Jotai and Preact Signals?

AFFiNE uses Jotai for global application state (workspaces, settings) where React's lifecycle integration is beneficial, and Preact Signals (@preact/signals-core) for high-frequency updates (drag-and-drop coordinates, animations) that require synchronous, fine-grained reactivity outside of React's render cycle. This dual approach optimizes performance for different interaction patterns.

How is styling handled across different parts of the AFFiNE frontend?

The frontend employs a hybrid styling architecture: Emotion handles dynamic, theme-dependent styles at runtime; Vanilla-Extract provides zero-runtime, type-safe CSS for static design tokens; and TailwindCSS is used specifically for the admin UI panels. This approach balances developer experience with runtime performance across different application surfaces.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →