Library / SDK
tinymce/tinymce-react avatar
tinymce/tinymce-react

tinymce-react: a thin React wrapper around an editor built for the DOM

Offical TinyMCE React component

1,069 stars167 forksTypeScriptMIT

At a glance

What is it?
The official TinyMCE React component packages the rich text editor as an `<Editor />` element that supports controlled and uncontrolled usage, loads TinyMCE from the Tiny Cloud CDN by default, and can be pointed at a self-hosted npm package instead. The wrapper itself is MIT licensed, while the TinyMCE core editor is GPL-2.0-or-later or commercial, and the peer dependency on the tinymce package is deliberately optional.
Who is it for?
Adopt tinymce-react if you are building a React application that needs a rich text editor and you either want the hosted Tiny Cloud setup or you already self-host TinyMCE, because the component handles the editor lifecycle, the plugin initialisation and the value binding in one place.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A React wrapper around an editor that was not written for React

The official React component for TinyMCE wraps TinyMCE, the rich text editor, as a React `<Editor />` component, supporting controlled and uncontrolled usage.

That is the whole of the project, and the interesting part is why it exists. TinyMCE is a long-standing editor that works by creating a DOM element and managing it directly, with its own lifecycle, its own event model and its own idea of when the content has changed. React has a different idea of all three. A component like this exists to bridge those two models so that an application does not have to.

The intended user is a React team that has decided on TinyMCE, usually because it is the editor the product already supports, and that wants to embed it without hand-writing the glue between a React re-render and an editor instance that owns its own DOM subtree.

The primary language is TypeScript, the licence is MIT with the licence text in `LICENSE.txt`, the default branch is `main`, and the last push was on 2026-10-12. The repository has no GitHub releases, so the changelog in `CHANGELOG.md` is where the version history lives. The package is published with the `files` list restricted to `lib`, the README, the changelog and the licence, and the build produces two outputs, a CommonJS build at `lib/cjs/main/ts/index.js` and an ES2015 module build at `lib/es2015/main/ts/index.js`.

The React peer dependency range is wide, `^19.0.0 || ^18.0.0 || ^17.0.1 || ^16.7.0`, and the same range is declared for `react-dom`, which tells you the component is expected to work across several major React versions rather than tracking the latest one.

Controlled and uncontrolled, and the reason the component exists

The README mentions controlled and uncontrolled usage in a single clause, and that clause is the design decision of the whole package.

An uncontrolled editor owns its own value. You give it an initial value and it manages subsequent edits, which is the simple case and matches how a plain textarea behaves. A controlled editor lifts the value into React state, so every keystroke produces a state update and the editor is re-rendered with the new content.

The reason this needs support in a component rather than in application code is that the two models interact badly with an imperative editor. If the editor is uncontrolled, the value React holds is stale unless you subscribe to the editor's change events. If the editor is controlled, you have to make sure the value going in is not re-normalised on every render, which is the classic bug in this kind of integration: the editor reformats the HTML it was given, React sees a difference, pushes the reformatted value back, and the cursor jumps.

A component in the middle can absorb that. It knows when the change came from the user and when it came from a prop, and it can hold the internal value separately from the external one.

That is the value proposition of the package, and it is a small one. It is also a real one, because the alternative for every team is to write this bridge themselves, and to get it wrong in the same way everyone else does.

The `init` prop in the example is the other half of the surface, and it is where the editor's own configuration lives, including the plugin list.

Cloud CDN by default, self-hosting as the alternative

By default, the component pulls TinyMCE from the Tiny Cloud CDN unless configured to use a different setup, such as self-hosting the tinymce npm package.

That default is a product decision, and it shapes the whole getting started path. The cloud route in the README is three steps. Sign up for a Tiny Cloud account to receive a Tiny Cloud API key, run `npm install @tinymce/tinymce-react` in your React project, and then include the component:

js
import React from 'react';
import { Editor } from '@tinymce/tinymce-react';

export default function App() {
  return (
    <>
      <h1>TinyMCE React demo</h1>
      <Editor
        apiKey="your-api-key"
        init={{
          plugins: 'lists link image table code help wordcount'
        }}
      />
    </>
  );
}

The last step of the quickstart is easy to miss: update the `apiKey` prop on the `Editor` component to include your Tiny Cloud API key. The key is a prop rather than a build-time setting, which is what lets the same bundle work across environments.

The self-hosted route is the one for teams that cannot send data to a third party, or that need the editor bundled rather than fetched. The README is candid that using TinyMCE from npm in a React project requires a couple of extra steps, and points at the separate documentation page for the self-hosted setup.

That phrase, a couple of extra steps, is doing some work in the documentation, and it is worth reading that page before choosing the route, because the choice determines whether you are shipping a licence obligation, a third party script tag, or a copy of the editor in your own bundle.

The optional peer dependency and the version matrix

The peer dependency block in the package metadata is the most technically informative part of the metadata.

The `tinymce` peer dependency is `^8.0.0 || ^7.0.0 || ^6.0.0 || ^5.5.1`, and it is marked optional in `peerDependenciesMeta`, with the `tinymce` entry set to optional. The only runtime dependency is `prop-types` at `^15.8.1`.

The optional marking is correct and deliberate. The component is a wrapper, and the wrapper does not need the editor package to be present, because in the default configuration the editor itself arrives from the Tiny Cloud CDN at runtime. The `tinymce` npm package is only needed when you self-host, and the peer range covers four major versions of the editor so that a project on an older TinyMCE can still use the component.

That range also tells you something about the support policy in practice. The component supports TinyMCE 5.5.1 through 8, and React 16.7 through 19, which is a wide compatibility surface for a package whose purpose is a thin wrapper. The cost of that width is that the component has to be tested against a matrix, and the repository's test tooling reflects that.

The React peer range and the `react-dom` peer range are identical, which is what you would expect from a component that renders a controlled DOM element, since a mismatch between the two React versions is a well known source of breakage in that pattern.

Bedrock for tests, Storybook for the demos

The scripts in the package metadata describe the development workflow.

The test runner is `bedrock-auto` in `chrome-headless` mode over the TypeScript test files in `src/test/ts/`, with a manual variant, `bedrock`, that opens a browser instead of running headless. Bedrock is the EpHoX test runner, and the dev dependencies show the surrounding stack: `@ephox/agar` and `@ephox/mcagar` for component testing, `@ephox/bedrock-client` and `@ephox/bedrock-server` for the runner itself, and `@ephox/katamari` for assertions. TinyMCE is developed by EpHoX, so the test stack is the company's own, and a component test for a rich text editor needs a real browser because there is no meaningful headless substitute for a contenteditable surface.

The build is two TypeScript compilations, one for the ES2015 module output and one for CommonJS, run after a clean step that removes `lib`.

For demos, the project uses Storybook. `storybook dev -p 6006` runs it locally, `storybook build` produces the static site, and the deploy script publishes it to GitHub Pages with the `tiny-bot` identity and a nojekyll flag. The published demos are at tinymce.github.io/tinymce-react, and the README points there for quick demos.

The tree also carries a `Jenkinsfile` and a `yarn.lock` alongside a `tslint.json` that predates the `eslint.config.mjs` in the same directory, which is the usual residue of a long-lived project migrating its linting. The `SECURITY.md` file is there as well, and the README makes a routing distinction worth noting: for issues concerning TinyMCE itself you should visit the TinyMCE repository, and this repository is for the component.

The wrapper is MIT, the editor is not

The licensing is the part to read twice, because the two things are licensed differently.

The `tinymce-react` component is licensed under the MIT License, with the details in the `LICENSE.txt` file. The README then adds the qualification: depending on use case, the TinyMCE core editor can be used under either GPL-2.0-or-later or a commercial license, with a pointer to the tinymce package for the details.

So the component that gives you the React binding is MIT, and the editor that it loads is not. If you use the cloud API key path, the editor is delivered by Tiny Cloud and the terms are the service terms. If you bundle the tinymce npm package into your application, you are distributing the core editor and you need to decide between accepting GPL-2.0-or-later and buying a commercial licence.

That decision has a practical consequence for a React product. GPL-2.0-or-later on a bundled component can be awkward for a closed source application, and the commercial path is priced per product rather than per developer, so the licensing conversation belongs at the start of the evaluation rather than at the end.

The MIT licence on the wrapper is genuinely permissive and carries no obligation beyond keeping the notice, and the component itself is a small amount of code with one runtime dependency. The risk in this stack is not the wrapper, it is the editor it depends on, and the default configuration, which sends your users' editor traffic to a third party CDN and requires an account with that vendor.

Where tinymce-react is the wrong choice

There are three cases where this component is the wrong answer, and the first is about editor size rather than React.

The first is a lightweight editing need. If your form field wants a paragraph of text, a rich text editor brings a full document model, a plugin system, a toolbar and a licence decision, where a textarea with a character counter is a hundred times smaller. Many products adopt a WYSIWYG because it is the default choice for a content field, not because the users need bold, tables, image embedding and a code view.

The second is a React application that must not talk to Tiny Cloud. The default configuration loads the editor from a third party CDN, which means a runtime dependency on someone else's infrastructure, an account to manage, and a key in your bundle. That is a reasonable default for a prototype and a poor one for a regulated product. The self-hosted route exists, but the README describes it as requiring a couple of extra steps, so the cost of that decision should be priced early.

The third is a codebase that is not React, or a team that wants the editor without the wrapper. The editor itself is a JavaScript library with its own integration guidance, and the wrapper exists to solve the React-specific lifecycle problem. If your framework is not React, this repository is not the right place to look, and the TinyMCE documentation is.

The alternative approach, and the one to weigh against, is to use the editor directly and write the binding yourself. The difference in approach is that you get a small amount of glue code and full control over the lifecycle, at the cost of solving the controlled and uncontrolled problem and the re-render normalisation issue for every application. If you have one editor in one application, that is a reasonable trade. If you have many, the component is the cheaper answer, and the version range it declares is the reason it can be the answer for many teams with different React versions.

Editorial conclusion

Adopt tinymce-react if you are building a React application that needs a rich text editor and you either want the hosted Tiny Cloud setup or you already self-host TinyMCE, because the component handles the editor lifecycle, the plugin initialisation and the value binding in one place. Do not adopt it if you need a lightweight plaintext or Markdown editor, since a full WYSIWYG is the wrong size of tool, and check the licence of the core editor rather than the wrapper, because the wrapper being MIT does not make TinyMCE itself MIT. Verify three things first: that your React version falls inside the peer dependency range the component supports, that you have decided between the cloud API key path and the self-hosted npm package, and that the editor licence choice, GPL-2.0-or-later or commercial, matches what your organisation is allowed to ship.

Frequently asked questions

What is the official TinyMCE React component?

It is the package `@tinymce/tinymce-react`, which wraps the TinyMCE rich text editor as a React `<Editor />` component and supports controlled and uncontrolled usage. It pulls TinyMCE from the Tiny Cloud CDN by default, or from a self-hosted npm package when configured that way.

How do I install and use tinymce-react?

Sign up for a Tiny Cloud account to get an API key, run `npm install @tinymce/tinymce-react`, import the Editor component, and pass the key in the `apiKey` prop with the editor configuration in the `init` prop, for example `init={{ plugins: 'lists link image table code help wordcount' }}`.

Which React and TinyMCE versions does tinymce-react support?

The peer dependencies allow React and react-dom at `^19.0.0 || ^18.0.0 || ^17.0.1 || ^16.7.0`, and TinyMCE at `^8.0.0 || ^7.0.0 || ^6.0.0 || ^5.5.1`. The tinymce peer dependency is marked optional, because the editor is loaded from the CDN in the default setup.

Can I use TinyMCE from npm instead of the Tiny Cloud CDN?

Yes. The README states that using TinyMCE from npm in a React project requires a couple of extra steps, and links to the self-hosted documentation page. The package metadata supports this, with the tinymce peer dependency marked optional so it is only needed when you bundle the editor yourself.

What licence applies to tinymce-react and to TinyMCE itself?

The tinymce-react component is MIT licensed. The TinyMCE core editor can be used under either GPL-2.0-or-later or a commercial license depending on the use case, and the README points to the tinymce package for the details.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. tinymce/tinymce-react on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tinymce-tinymce-react.svg)](https://hysenlabs.com/projects/tinymce-tinymce-react)

Was this useful?