Want to contribute to Slate? That would be awesome!
Reporting Bugs
If you run into any weird behavior while using Slate, feel free to open a new issue in this repository! Please run a search before opening a new issue, to make sure that someone else hasn't already reported or solved the bug you've found.
Any issue you open must include:
- A JSFiddle that reproduces the bug with a minimal setup.
- A GIF showing the issue in action. (Using something like RecordIt.)
- A clear explanation of what the issue is.
Here's a JSFiddle template for Slate to get you started:
Asking Questions
ℹ️ If you're looking for help with the Slate CRM platform, that's an unrelated project to us.
We've also got a Slate Slack team where you can ask questions and get answers from other people using Slate:
Please use the Slack instead of asking questions in issues, since we want to reserve issues for keeping track of bugs and features. We close questions in issues so that maintaining the project isn't overwhelming.
Submitting Pull Requests
All pull requests are super welcomed and greatly appreciated! Issues in need of a solution are marked with a ♥ help label if you're looking for somewhere to start.
Please include tests and docs with every pull request!
Repository Setup
The Slate repository is a Bun workspace orchestrated with Turbo. Unlike more traditional repositories, this means that the repository must be built in order for tests, linting, or other common development activities to function as expected.
To run the build, you need to have the Slate repository cloned to your computer. After that, you need to cd into the directory where you cloned it, and install the dependencies with Bun and build the monorepo:
bun install
bun run buildbun install
bun run buildRunning Examples
To run the examples, start by building the monorepo as described in the Repository Setup section.
Then you can start the examples server with:
bun devbun devRunning Tests
To run the tests, start by building the monorepo as described in the Repository Setup section.
The fast local repository gate is:
bun checkbun checkIt runs lint, typecheck, and tests. Playwright integration coverage lives in the fuller gates:
bun check:ci
bun check:fullbun check:ci
bun check:fullPackage typechecking pulls the package build prerequisites through Turbo instead of front-loading a full root build. For purely mechanical formatting fixes, run:
bun lint:fixbun lint:fixIf you only want to rerun the tests, use:
bun run testbun run testThat runs the full package test graph:
bun testfor Bun-owned lanesbun test:vitestfor the@platejs/slate-reactDOM lane
If you only want the Bun-owned package tests, use:
bun testbun testIf you only want the @platejs/slate-react Vitest lane, use:
bun test:vitestbun test:vitestIf you need to debug something, you can add a debugger line to the source, and then run bun test:inspect.
If you only want to run a specific Bun-owned test or tests, use Bun's test-name filtering:
bun test ./packages/slate/test/index.spec.ts --test-name-pattern "Editor\\.above"bun test ./packages/slate/test/index.spec.ts --test-name-pattern "Editor\\.above"If you only want to run a specific @platejs/slate-react Vitest test or tests, run Vitest directly:
cd ./packages/slate-react && bun test:vitest -- test/react-editor.test.tsx -t "should not trigger onValueChange"cd ./packages/slate-react && bun test:vitest -- test/react-editor.test.tsx -t "should not trigger onValueChange"If you only want the lint gate, use:
bun lintbun lintRunning integration tests
To run integrations with Playwright, either:
- run
bun devand thenbun run playwrightin a separate session - or run
bun test:integration-local
The local integration command manages its own exported-site server, so it does not need the normal dev server to already be running.
For a broad local sweep that should not block your current terminal or agent thread, start it asynchronously:
bun test:integration-local:asyncbun test:integration-local:asyncThe async command accepts Playwright targets and flags when you want a narrower background run:
bun test:integration-local:async playwright/integration/examples/richtext.test.ts --project=chromiumbun test:integration-local:async playwright/integration/examples/richtext.test.ts --project=chromiumThen inspect the latest run later:
bun test:integration-local:pickup
bun test:integration-local:status
bun test:integration-local:failuresbun test:integration-local:pickup
bun test:integration-local:status
bun test:integration-local:failuresAsync runs write ignored artifacts under .tmp/integration-runs/<run-id>/,
including status.json, pickup.md, raw.log, report.json, and
failures.md. Only one async integration run starts at a time. Start from
pickup.md or bun test:integration-local:pickup when resuming a previous
run.
The async command is shared across local agents. If the same command is already
running for the current source stamp, bun test:integration-local:async reuses
that run and prints its pickup/status commands instead of starting another
Playwright sweep. If the same command already completed for the current source
stamp, it prints the cached pickup. A different command while another run is
active is rejected clearly; use pickup first, then start the different sweep
after the active one finishes.
Running integration tests in Docker
If tests fail on CI but pass locally (often due to OS differences), you can run tests in a Docker container that replicates the same environment as CI.
Prerequisites: The project must be built first (same as running tests locally).
bun test:integration-dockerbun test:integration-dockerThe script will automatically:
- Start the development server (if not already running)
- Run tests inside a Docker container
- Stop the server when tests complete
You can also pass additional arguments to the test runner. For example, to run a specific test file:
bun test:integration-docker playwright/integration/slate-react/selection.test.tsbun test:integration-docker playwright/integration/slate-react/selection.test.tsOr run a specific browser project:
bun test:integration-docker --project=chromiumbun test:integration-docker --project=chromiumYou can combine arguments as well:
bun test:integration-docker playwright/integration/examples/richtext.test.ts --project chromiumbun test:integration-docker playwright/integration/examples/richtext.test.ts --project chromiumTesting Input Methods
Here's a helpful page detailing how to test various input scenarios on Windows, Mac and Linux.
Android tests
When making changes that affect Android or IME behavior, test against
/examples/android-tests.
That page links to the placeholder, inline-edge, and void-edge examples.
Publishing Releases
Publishing Normal @latest Release
Slate uses Changesets for publishing, so run:
bun release:latestbun release:latestThis automatically runs the prerelease script first so the build, test, and lint gate is green before publishing.
Publishing @next Release
If we are unsure as to the stability of a release because there are significant changes and/or particularly complex changes, release with the @next tag.
bun release:nextbun release:nextPublishing @experimental Release
If you need to create an experimental release to see how a published package will behave during an actual publish, release with the @experimental tag. End users should have no expectation that an @experimental release will be usable.
bun release:experimentalbun release:experimentalRunning Prerelease Script
If we want to make sure that Slate code follows the preparations for a release but without actually publishing, run:
bun prereleasebun prereleaseWhich will build, test and lint Slate code.
On This Page
Reporting BugsAsking QuestionsSubmitting Pull RequestsRepository SetupRunning ExamplesRunning TestsRunning integration testsRunning integration tests in DockerTesting Input MethodsAndroid testsPublishing ReleasesPublishing Normal @latest ReleasePublishing @next ReleasePublishing @experimental ReleaseRunning Prerelease Script