-
Notifications
You must be signed in to change notification settings - Fork 37
Add onboarding documentation for new developers #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
iamalexndr
wants to merge
2
commits into
koldovsky:main
Choose a base branch
from
iamalexndr:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,238 @@ | ||
| # Excalidraw Developer Onboarding | ||
|
|
||
| ## What is Excalidraw? | ||
|
|
||
| Excalidraw is an open-source virtual whiteboard for sketching diagrams, wireframes, and ideas in a hand-drawn aesthetic. It runs entirely in the browser, works offline as a PWA, and supports real-time collaboration with end-to-end encryption. | ||
|
|
||
| The project serves two purposes at once: it's a **publishable React component library** (`@excalidraw/excalidraw` on npm) that other products can embed, and it's the full **excalidraw.com web application** — both living in the same monorepo. This dual nature shapes almost every structural decision in the codebase. | ||
|
|
||
| --- | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| | Layer | Technology | | ||
| |---|---| | ||
| | UI | React 19, TypeScript 5.9 (strict) | | ||
| | State | Jotai (atomic state) | | ||
| | Canvas | Canvas API + RoughJS (hand-drawn style) | | ||
| | Styling | SASS/SCSS | | ||
| | Build (app) | Vite 5 | | ||
| | Build (packages) | esbuild | | ||
| | Testing | Vitest + @testing-library/react | | ||
| | Linting | ESLint + Prettier | | ||
| | Collaboration | Socket.io + Firebase | | ||
| | Monorepo | Yarn Workspaces | | ||
|
|
||
| --- | ||
|
|
||
| ## Repository Structure | ||
|
|
||
| The repo is cleanly split between the library packages and the application: | ||
|
|
||
| ``` | ||
| excalidraw/ | ||
| ├── packages/ | ||
| │ ├── excalidraw/ # @excalidraw/excalidraw — the npm library (main package) | ||
| │ ├── common/ # @excalidraw/common — shared constants, color utils | ||
| │ ├── element/ # @excalidraw/element — shape types, geometry, mutations | ||
| │ ├── math/ # @excalidraw/math — 2D math, vectors, transforms | ||
| │ └── utils/ # @excalidraw/utils — clipboard, file export/import | ||
| ├── excalidraw-app/ # The excalidraw.com application | ||
| │ ├── App.tsx # App-level wrapper | ||
| │ ├── collab/ # Real-time collaboration | ||
| │ └── data/ # Firebase, local storage, file management | ||
| ├── examples/ # Integration examples (Next.js, plain HTML) | ||
| ├── scripts/ # Build and release scripts | ||
| └── dev-docs/ # Developer documentation | ||
| ``` | ||
|
|
||
| The most important mental model: **`packages/` is the library, `excalidraw-app/` is the product**. Any feature that belongs in the editor itself — a new tool, a rendering fix, a new element type — lives under `packages/`. Anything that's specific to excalidraw.com — collaboration, Firebase, PWA registration, analytics — lives in `excalidraw-app/`. When in doubt, ask whether the change would make sense if someone embedded the library in their own app. If yes, it belongs in `packages/`. | ||
|
|
||
| The packages form a layered dependency chain. Lower-level packages know nothing about higher-level ones: | ||
|
|
||
| ``` | ||
| @excalidraw/excalidraw ← main library, depends on everything below | ||
| ├── @excalidraw/common ← foundational constants and utilities | ||
| ├── @excalidraw/element ← shape types and geometry | ||
| │ ├── @excalidraw/math ← pure 2D math | ||
| │ └── @excalidraw/common | ||
| └── @excalidraw/utils ← export/import, clipboard | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Architecture | ||
|
|
||
| ### State Management | ||
|
|
||
| The editor's entire runtime state — the active tool, zoom level, pan offset, selected elements, current stroke color, whether the grid is on, and dozens of other properties — lives in a single `AppState` object defined in [packages/excalidraw/types.ts](packages/excalidraw/types.ts). This object is exposed as Jotai atoms, not passed through props or stored in a Redux slice. | ||
|
|
||
| The two relevant store files are: | ||
| - [packages/excalidraw/editor-jotai.ts](packages/excalidraw/editor-jotai.ts) — atoms for the library-level editor state | ||
| - [excalidraw-app/app-jotai.ts](excalidraw-app/app-jotai.ts) — atoms for app-specific state like collaboration and theme | ||
|
|
||
| Jotai was chosen over Redux because it allows fine-grained subscriptions — a component that only cares about the zoom level won't re-render when the stroke color changes. | ||
|
|
||
| ### Rendering | ||
|
|
||
| The canvas is rendered in two separate layers, both in [packages/excalidraw/scene/](packages/excalidraw/scene/): | ||
|
|
||
| **[interactiveScene.ts](packages/excalidraw/scene/interactiveScene.ts)** handles the live canvas the user interacts with. It redraws on every state change and includes selection handles, hover states, and in-progress drawing feedback. | ||
|
|
||
| **[staticScene.ts](packages/excalidraw/scene/staticScene.ts)** produces a deterministic, interaction-free render of the elements. This is what gets used when exporting to PNG. **[staticSvgScene.ts](packages/excalidraw/scene/staticSvgScene.ts)** does the same for SVG exports. | ||
|
|
||
| RoughJS sits between the canvas API and the element geometry — it intercepts draw calls and adds the hand-drawn jitter. This means the aesthetic is applied uniformly across all shape types without each one needing to implement it. | ||
|
|
||
| ### Element System | ||
|
|
||
| Every shape on the canvas is an *element* — a plain TypeScript object with a type, geometry, style properties, and a version number. Elements are **immutable**: you never mutate one in place. Any change produces a new object with an incremented version. This constraint is load-bearing — the version number is how the collaboration layer detects and resolves conflicts, and how the undo/redo history tracks changes. | ||
|
|
||
| Element types (`rectangle`, `diamond`, `ellipse`, `arrow`, `line`, `freedraw`, `text`, `image`, `frame`, `embeddable`) and their shared and specific properties are all defined in [packages/element/types.ts](packages/element/types.ts). | ||
|
|
||
| ### Actions System | ||
|
|
||
| Rather than scattering editor logic across components, all discrete editor operations are defined as *actions* in [packages/excalidraw/actions/](packages/excalidraw/actions/). Each action file (e.g. `actionAlign.tsx`, `actionClipboard.tsx`) declares what it does, what keyboard shortcut triggers it, where it appears in menus, and the handler function that executes it. This makes it straightforward to add a new editor operation without touching component code — you define the action and register it. | ||
|
|
||
| ### Data Flow | ||
|
|
||
| Understanding how a user interaction becomes a canvas update is useful early on: | ||
|
|
||
| ``` | ||
| User interaction (click, keypress, drag) | ||
| → Action handler (packages/excalidraw/actions/) | ||
| → Jotai atom update (AppState and/or elements array) | ||
| → React re-render | ||
| → Canvas redraw (interactiveScene.ts) | ||
| ``` | ||
|
|
||
| Saving a drawing serializes the elements and AppState to compressed JSON via [data/encode.ts](packages/excalidraw/data/encode.ts), producing a `.excalidraw` file. Loading runs the reverse through [data/restore.ts](packages/excalidraw/data/restore.ts), which also handles schema migrations so old files continue to open correctly. | ||
|
|
||
| --- | ||
|
|
||
| ## First Steps | ||
|
|
||
| ### 1. Prerequisites | ||
|
|
||
| You need **Node.js >= 18.0.0** and **Yarn**. If you don't have Yarn, the easiest way is via corepack which ships with Node: | ||
|
|
||
| ```bash | ||
| corepack enable | ||
| ``` | ||
|
|
||
| Verify your setup: | ||
|
|
||
| ```bash | ||
| node --version # should be >= 18.0.0 | ||
| yarn --version # should print a version number | ||
| ``` | ||
|
|
||
| ### 2. Clone and install | ||
|
|
||
| ```bash | ||
| git clone https://github.com/excalidraw/excalidraw.git | ||
| cd excalidraw | ||
| yarn install | ||
| ``` | ||
|
|
||
| `yarn install` from the root installs dependencies for all workspaces at once. You don't need to `cd` into individual packages — Yarn Workspaces handles everything from the root. This step also sets up the symlinks between packages so that, for example, `excalidraw-app` can import `@excalidraw/excalidraw` directly from source without a build step. | ||
|
|
||
| ### 3. Start the dev server | ||
|
|
||
| ```bash | ||
| yarn start | ||
| # App runs at http://localhost:3000 | ||
| ``` | ||
|
|
||
| This starts the `excalidraw-app` Vite dev server with hot module replacement. Because the packages are symlinked, any change you make in `packages/excalidraw/` or any other package is reflected in the browser immediately — no separate package build step needed during development. | ||
|
|
||
| ### 4. Explore the codebase | ||
|
|
||
| A good starting path through the code: | ||
|
|
||
| 1. [packages/excalidraw/index.tsx](packages/excalidraw/index.tsx) — see what the library exposes publicly | ||
| 2. [packages/excalidraw/types.ts](packages/excalidraw/types.ts) — read `AppState` and `ExcalidrawProps` to understand what drives the editor | ||
| 3. [packages/excalidraw/components/App.tsx](packages/excalidraw/components/App.tsx) — the main editor component where most interaction logic lives | ||
| 4. [packages/excalidraw/actions/](packages/excalidraw/actions/) — pick any action file to see the pattern for adding editor operations | ||
|
|
||
| ### 5. Make a change and verify it | ||
|
|
||
| Before touching anything real, do a quick smoke test to confirm your environment works end-to-end: | ||
|
|
||
| ```bash | ||
| yarn test:typecheck # TypeScript compiles cleanly | ||
| yarn test:update # All tests pass (updates snapshots if needed) | ||
| yarn fix # No formatting or lint issues | ||
| ``` | ||
|
|
||
| All three should exit cleanly. If they do, your setup is correct and you're ready to contribute. Run the same three commands before every commit — the CI enforces all of them on every PR. | ||
|
|
||
| --- | ||
|
|
||
| ## Development Commands | ||
|
|
||
| ```bash | ||
| yarn start # Dev server for excalidraw-app (localhost:3000) | ||
| yarn test:typecheck # TypeScript compilation check | ||
| yarn test:update # Run all tests, update snapshots | ||
| yarn test:app # Run Vitest in watch mode | ||
| yarn fix # Auto-fix ESLint + Prettier issues | ||
| yarn build # Production build of excalidraw-app | ||
| yarn build:packages # Compile all library packages with esbuild | ||
| ``` | ||
|
|
||
| **Before every commit:** | ||
| ```bash | ||
| yarn test:typecheck && yarn test:update && yarn fix | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Key Files to Know | ||
|
|
||
| | File | What it does | | ||
| |---|---| | ||
| | [packages/excalidraw/index.tsx](packages/excalidraw/index.tsx) | Library entry point — exports the `<Excalidraw>` component | | ||
| | [packages/excalidraw/types.ts](packages/excalidraw/types.ts) | Defines `AppState`, `ExcalidrawProps`, and all core types | | ||
| | [packages/excalidraw/components/App.tsx](packages/excalidraw/components/App.tsx) | The main editor component — most interaction logic lives here | | ||
| | [packages/excalidraw/editor-jotai.ts](packages/excalidraw/editor-jotai.ts) | Jotai store setup for the library | | ||
| | [packages/excalidraw/scene/interactiveScene.ts](packages/excalidraw/scene/interactiveScene.ts) | Live canvas rendering | | ||
| | [packages/excalidraw/data/restore.ts](packages/excalidraw/data/restore.ts) | Deserializes and migrates saved drawings | | ||
| | [packages/element/types.ts](packages/element/types.ts) | All element type definitions | | ||
| | [excalidraw-app/App.tsx](excalidraw-app/App.tsx) | Application-level wrapper around the library | | ||
| | [excalidraw-app/collab/Collab.tsx](excalidraw-app/collab/Collab.tsx) | Real-time collaboration logic | | ||
|
|
||
| --- | ||
|
|
||
| ## Conventions | ||
|
|
||
| **Imports:** Always use package aliases rather than relative paths across package boundaries — `import { ... } from "@excalidraw/common"`, not `../../common/src/...`. Use `import type { ... }` for TypeScript-only imports (ESLint enforces this). Import order goes: builtins → external packages → `@excalidraw/*` packages → parent directories → siblings. | ||
|
|
||
| **Do not import directly from `jotai`** — always go through `editor-jotai` or `app-jotai`. These wrappers configure the correct store instance. | ||
|
|
||
| **Naming:** React components use `PascalCase.tsx`, utility modules use `camelCase.ts`, and test files sit alongside their source as `FileName.test.ts`. | ||
|
|
||
| **Elements are immutable.** Never mutate an element object in place. Always create a new object and increment its `version` field. Code that violates this will subtly break undo/redo and collaboration. | ||
|
|
||
| --- | ||
|
|
||
| ## CI Checks | ||
|
|
||
| All of these must pass before a PR can merge: | ||
|
|
||
| | Check | What it runs | | ||
| |---|---| | ||
| | TypeScript | `yarn test:typecheck` | | ||
| | Lint | `yarn test:code` (ESLint) | | ||
| | Formatting | `yarn test:other` (Prettier) | | ||
| | Tests | `yarn test:app` (Vitest) | | ||
| | Bundle size | size-limit action — flags regressions automatically | | ||
| | PR title | must follow the semantic commit format (e.g. `feat:`, `fix:`) | | ||
|
|
||
| --- | ||
|
|
||
| ## Getting Help | ||
|
|
||
| - **Docs:** https://docs.excalidraw.com | ||
| - **Contributing guide:** https://docs.excalidraw.com/docs/introduction/contributing | ||
| - **Discord:** https://discord.gg/UexuTaE | ||
| - **Issues:** https://github.com/excalidraw/excalidraw/issues | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: koldovsky/is-01-docs
Length of output: 664
Convert file path references to absolute GitHub URLs or move documentation to Excalidraw repository.
This documentation contains relative file path references throughout (lines 68–72, 78–83, 90–91, 107–108, 152–155, 194–202) that assume the document lives in the Excalidraw repository root. However, it's being added to
koldovsky/is-01-docs, a separate repository where none of these paths exist:packages/excalidraw/— not foundexcalidraw-app/— not foundtypes.ts,editor-jotai.ts,App.tsx, etc.) — not foundThese links will be broken and unusable. Either:
https://github.com/excalidraw/excalidraw/blob/main/packages/excalidraw/types.ts)🤖 Prompt for AI Agents