AGENTS.md · element-hq/element-web
AGENTS.md from element-hq/element-web: 195 lines covering Repository layout, Commands, Running unit tests, Running e2e tests and more.
Template by element-hq
Chalk Labs
Need help deploying AGENTS.md · element-hq/element-web?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Claude Code. Read the repository before you give any tool access to your data.
curl -fsSL https://raw.githubusercontent.com/element-hq/element-web/develop/AGENTS.md -o AGENTS.md
Read it and adapt it to your project before use.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: prompt-engineering, code-generation
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
What's in it
An AGENTS.md file: instructions coding agents (Codex, Cursor, Copilot, Gemini CLI and others) read before working in a repo.
- For
- Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Claude Code
- Length
- 195 lines
- File
AGENTS.md
Sections
- Repository layout
- Commands
- Running unit tests
- Running e2e tests
- Writing UI code: MVVM
- Code style
- Comments
- Tests are required
- Commits
- Pull requests
First lines (shown under the repo's AGPL-3.0 license)
# AGENTS.md
Instructions for AI coding agents working in this repository. Humans should read
[CONTRIBUTING.md](./CONTRIBUTING.md) and [code_style.md](./code_style.md) instead — this file summarises those and
adds the things that are easy to get wrong here.
## Repository layout
A pnpm + nx monorepo (`element-web-monorepo`). Workspaces are `apps/*`, `packages/*`, `modules` and `modules/*`.
| Path | Contents |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `apps/web` | The Element Web app. Also hosts view models (`src/viewmodels/`) and the Playwright e2e suite (`playwright/`). |
| `apps/desktop` | Element Desktop. |
| `packages/shared-components` | Published UI component library, `@element-hq/web-shared-components`. New views go here. |
| `packages/module-api`, `packages/shared-utils`, `packages/shared-types`, `packages/playwright-common` | Published support packages. |
| `modules/*` | Optional runtime modules (banner, widget toggles, …). |
| `docs/` | VitePress docs site. |
There is **no** top-level `playwright/` directory; `docs/playwright.md` uses paths relative to `apps/web`.
## Commands
Run from the repo root unless stated otherwise.
| Task | Command |
| ------------------------------------------------------ | ------------------------------------------------- |
| Full lint (types, format, js, styles, workflows, knip) | `pnpm lint` |
| Format | `pnpm lint:fmt:fix` |
| Lint JS/TS | `pnpm lint:js:fix` |
| Typecheck | `pnpm -r --workspace-concurrency=1 lint:types` |
| All unit tests | `pnpm test:unit` |
| Regenerate i18n strings | `pnpm i18n` |
| Start the app | `cd apps/web && pnpm start` |
| Storybook | `cd packages/shared-components && pnpm storybook` |
Formatting is **oxfmt** and linting is **oxlint**. This repo does not use prettier or eslint — running prettier here
reformats files against the project style and reports failures that do not exist.
Run `pnpm i18n` after adding or changing any translated string.
## Running unit tests
Both jest and vitest are in use; a migration to vitest is in progress. **The file path decides the runner.**
| Test file | Runner | Command |
| ---------------------------------------------- | ---------------- | ----------------------------------------------------------- |
| `apps/web/test/**/*-test.[tj]s?(x)` | jest | `cd apps/web && pnpm jest <path>` |
| `apps/web/src/**/*.test.{ts,tsx}` | vitest | `pnpm vitest run <path>` |
| `apps/web/src/**/*.test.browser.{ts,tsx}` | vitest (browser) | `pnpm vitest run --project web-browser <path>` |
| `packages/shared-components/src/**/*.test.tsx` | vitest | `cd packages/shared-components && pnpm test:unit -- <path>` |
Notes:
- Write new tests as co-located vitest `*.test.ts(x)` next to the source file. The `apps/web/test/` jest tree is
legacy; extend it only when the file you are changing already lives there.
- Vitest runs with `globals: false`, so import `describe`, `it`, `expect` and friends explicitly.
- `packages/shared-components` is excluded from the root vitest config and must be run from its own directory.
- "No test files found" means you picked the wrong runner or path. It is not a pass.
- Prefer extending an existing test over adding a new file when covering a close variant of existing behaviour.
## Running e2e tests
Specs live in `apps/web/playwright/e2e`. See [docs/playwright.md](./docs/playwright.md) for the full guide.
```sh
cd apps/web && pnpm test:playwright -- playwright/e2e/<spec>.spec.ts --project=Chrome
```
- Requires a container runtime (Docker/Podman/Colima) for the homeserver testcontainers.
- On macOS, add `--ignore-snapshots`. Screenshot baselines are only committed for Linux, so comparisons fail locally.
- Never run `pnpm test:playwright:screenshots` locally to "fix" a screenshot diff — it writes host-rendered baselines.
Screenshots are updated in the Docker environment, which CI matches.
- The config uses `reuseExistingServer: true`. Check `lsof -ti:8080` first; a stale dev server silently serves the
tests. Port-bind errors are an infrastructure problem, not a test failure.
- Locate elements by role, label and accessible name, not CSS classes.
- Tag any test using `toMatchScreenshot` with `@screenshot`.
### Reviewing updated screenshots
Updated baselines are part of the diff and must be reviewed, not accepted blindly. Before committing any changed
`.png` under `apps/web/playwright/snapshots/` or `packages/shared-components/__vis__/`:
- Look at the image. Confirm every visible difference is one your change was meant to produce.
- Treat anything else as a regression until proven otherwise: shifted or clipped layout, changed spacing or font,
missing or duplicated elements, a wrong theme, an untranslated or placeholder string, a loading or error state
captured instead of the real content.
- Unrelated files changing baselines is a signal in itself — if a screenshot moved for a component you did not
touch, find out why rather than committing it.
- Never regenerate baselines to make a failing test pass. A diff you cannot explain is a bug in the change.
## Writing UI code: MVVM
New UI follows MVVM. Full details in [docs/MVVM.md](./docs/MVVM.md); the shape for a feature `Foo`:
**View** — `packages/shared-components/src/<domain>/FooView/` containing `FooView.tsx`, `FooView.module.css`,
`FooView.test.tsx`, `FooView.stories.tsx` and `index.ts`. The view declares the contract:
```tsx
export interface FooViewSnapshot { title: string }
interface FooViewActions { setTitle: (title: string) => void }
export type FooViewModel = ViewModel<FooViewSnapshot, FooViewActions>;
export function FooView({ vm }: { vm: FooViewModel }): JSX.Element {
const { title } = useViewModel(vm);
...
}
```
Views are dumb: they read the snapshot and call actions, nothing else. Develop them in Storybook.
**ViewModel** — `apps/web/src/viewmodels/<domain>/FooViewModel.ts`, a class extending `BaseViewModel` that implements
the interface from the view. `apps/web/src/components/viewmodels/` is the deprecated v1 location; do not add there.
Rules that are easy to miss:
- Define actions as arrow-function class properties (`public doThing = (): void => {}`) so `this` survives being
passed as a callback.
- Use `this.snapshot.merge({ field })` for partial updates. `merge` already skips emitting when nothing changed, so
do not add equality guards around it, and do not recompute the whole snapshot for one field.
- Track listeners and sub-view-models with `this.disposables.trackListener(...)` / `this.disposables.track(...)`.
- A non-MVVM function component owning a view model should create it with `useCreateAutoDisposedViewModel`; a class
component creates it in `componentDidMount` and disposes it in `componentWillUnmount`.
Reference implementation: `packages/shared-components/src/room-list/RoomListSearchView/` with
`apps/web/src/viewmodels/room-list/RoomListSearchViewModel.ts`.
## Code style
Read [code_style.md](./code_style.md). The points most often missed:
- **Every new file needs a copyright header**, enforced by oxlint (`element-call/copyright-header`). Lint fails
without it. Substitute the actual current year — check it rather than copying a year from another file — and leave
headers on existing files alone:
```
/*
Copyright <current year> <copyright holder>
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
```
The holder is `Element Creations Ltd.` **only for contributions made as part of Element**. An external
contributor puts their own name or their company's there instead. Note that `oxlint --fix` inserts the Element
line unconditionally, so external contributors should write the header by hand and check what the autofix added.
- TypeScript only, named exports only — avoid `export default`.
- 4-space indent, 120-column limit, double quotes, semicolons.
- Declare member visibility (`public`/`private`/`protected`) on class members.
- Avoid `any`; if unavoidable, comment why.
- Roughly one interface, class or enum per file, named after the file.
- Never mix cosmetic and functional changes.
- **Styles:** `apps/web` uses PostCSS (`res/css/**/_Component.pcss`, `mx_`-prefixed classes).
`packages/shared-components` uses CSS modules (`Component.module.css`, semantic camelCase class names, no `mx_`
prefix) imported as `styles`. Use Compound design tokens (`var(--cpd-color-…)`, `var(--cpd-space-…)`) for all values.
- Prefer Compound typography components over raw text elements, and `Flex`/`Box` from shared-components over raw
flexbox markup.Signals
- Stars
- 13,612
- Forks
- 2,802
- Open issues
- 3,584
- Last commit (this folder)
- Oct 2, 2026 (29 days ago)
- Last release
- Oct 16, 2026 (15 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- AGPL-3.0
- Repository
- github.com/element-hq/element-web
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get AGENTS.md · element-hq/element-web running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
AGENTS.md · element-hq/element-web. Agent Mart. https://agentmart.dev/templates/element-hq-element-web-agents-md (last updated 2026-10-24)