diff --git a/docs/src/demos/Examples/Default/React/index.jsx b/docs/src/demos/Examples/Default/React/index.jsx
new file mode 100644
index 00000000..8fb12656
--- /dev/null
+++ b/docs/src/demos/Examples/Default/React/index.jsx
@@ -0,0 +1,167 @@
+import React, { useState } from 'react'
+import { defaultExtensions } from '@tiptap/starter-kit'
+import { useEditor, Editor } from '@tiptap/react'
+import './styles.scss'
+
+// useEditor only works for child components of
+const MenuBar = () => {
+ const editor = useEditor()
+
+ return (
+ <>
+ editor.chain().focus().toggleItalic().run()}
+ className={`${editor.isActive('italic') ? 'active' : ''}`}
+ >
+ italic
+
+ editor.chain().focus().toggleStrike().run()}
+ className={`${editor.isActive('strike') ? 'active' : ''}`}
+ >
+ strike
+
+ editor.chain().focus().toggleCode().run()}
+ className={`${editor.isActive('code') ? 'active' : ''}`}
+ >
+ code
+
+ editor.chain().focus().unsetAllMarks().run()}>
+ clear marks
+
+ editor.chain().focus().clearNodes().run()}>
+ clear nodes
+
+ editor.chain().focus().setParagraph().run()}
+ className={`${editor.isActive('paragraph') ? 'active' : ''}`}
+ >
+ paragraph
+
+ editor.chain().focus().toggleHeading({ level: 1 }).run()}
+ className={`${editor.isActive('heading', { level: 1 }) ? 'active' : ''}`}
+ >
+ h1
+
+ editor.chain().focus().toggleHeading({ level: 2 }).run()}
+ className={`${editor.isActive('heading', { level: 2 }) ? 'active' : ''}`}
+ >
+ h2
+
+ editor.chain().focus().toggleHeading({ level: 3 }).run()}
+ className={`${editor.isActive('heading', { level: 3 }) ? 'active' : ''}`}
+ >
+ h3
+
+ editor.chain().focus().toggleHeading({ level: 4 }).run()}
+ className={`${editor.isActive('heading', { level: 4 }) ? 'active' : ''}`}
+ >
+ h4
+
+ editor.chain().focus().toggleHeading({ level: 5 }).run()}
+ className={`${editor.isActive('heading', { level: 5 }) ? 'active' : ''}`}
+ >
+ h5
+
+ editor.chain().focus().toggleHeading({ level: 6 }).run()}
+ className={`${editor.isActive('heading', { level: 6 }) ? 'active' : ''}`}
+ >
+ h6
+
+ editor.chain().focus().toggleBulletList().run()}
+ className={`${editor.isActive('bulletList') ? 'active' : ''}`}
+ >
+ bullet list
+
+ editor.chain().focus().toggleOrderedList().run()}
+ className={`${editor.isActive('orderedList') ? 'active' : ''}`}
+ >
+ ordered list
+
+ editor.chain().focus().toggleCodeBlock().run()}
+ className={`${editor.isActive('codeBlock') ? 'active' : ''}`}
+ >
+ code block
+
+ editor.chain().focus().toggleBlockquote().run()}
+ className={`${editor.isActive('blockquote') ? 'active' : ''}`}
+ >
+ blockquote
+
+ editor.chain().focus().setHorizontalRule().run()}>
+ horizontal rule
+
+ editor.chain().focus().setHardBreak().run()}>
+ hard break
+
+ editor.chain().focus().undo().run()}>
+ undo
+
+ editor.chain().focus().redo().run()}>
+ redo
+
+ editor.chain().focus().toggleBold().run()}
+ className={`${editor.isActive('bold') ? 'is-active' : ''}`}
+ >
+ bold
+
+ >
+ )
+}
+
+export default () => {
+ const [value, setValue] = useState(`
+
+ Hi there,
+
+
+ this is a basic basic example of tiptap . Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists:
+
+
+
+ That’s a bullet list with one …
+
+
+ … or two list items.
+
+
+
+ Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block:
+
+ body {
+ display: none;
+}
+
+ I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too.
+
+
+ Wow, that’s amazing. Good work, boy! 👏
+
+ — Mom
+
+`)
+
+ return (
+ <>
+
+
+
+ >
+ )
+}
diff --git a/docs/src/demos/Examples/Default/React/styles.scss b/docs/src/demos/Examples/Default/React/styles.scss
new file mode 100644
index 00000000..dc996245
--- /dev/null
+++ b/docs/src/demos/Examples/Default/React/styles.scss
@@ -0,0 +1,55 @@
+/* Basic editor styles */
+.ProseMirror {
+ > * + * {
+ margin-top: 0.75em;
+ }
+
+ ul,
+ ol {
+ padding: 0 1rem;
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ line-height: 1.1;
+ }
+
+ code {
+ background-color: rgba(#616161, 0.1);
+ color: #616161;
+ }
+
+ pre {
+ background: #0D0D0D;
+ color: #FFF;
+ font-family: 'JetBrainsMono', monospace;
+ padding: 0.75rem 1rem;
+ border-radius: 0.5rem;
+
+ code {
+ color: inherit;
+ background: none;
+ font-size: 0.8rem;
+ }
+ }
+
+ img {
+ max-width: 100%;
+ height: auto;
+ }
+
+ blockquote {
+ padding-left: 1rem;
+ border-left: 2px solid rgba(#0D0D0D, 0.1);
+ }
+
+ hr {
+ border: none;
+ border-top: 2px solid rgba(#0D0D0D, 0.1);
+ margin: 2rem 0;
+ }
+}
diff --git a/docs/src/demos/Examples/Default/index.spec.js b/docs/src/demos/Examples/Default/Vue/index.spec.js
similarity index 86%
rename from docs/src/demos/Examples/Default/index.spec.js
rename to docs/src/demos/Examples/Default/Vue/index.spec.js
index 33a3d128..8580f40a 100644
--- a/docs/src/demos/Examples/Default/index.spec.js
+++ b/docs/src/demos/Examples/Default/Vue/index.spec.js
@@ -1,6 +1,6 @@
-context('/demos/Examples/Default', () => {
+context('/demos/Examples/Default/Vue', () => {
before(() => {
- cy.visit('/demos/Examples/Default')
+ cy.visit('/demos/Examples/Default/Vue')
})
beforeEach(() => {
diff --git a/docs/src/demos/Examples/Default/index.vue b/docs/src/demos/Examples/Default/Vue/index.vue
similarity index 100%
rename from docs/src/demos/Examples/Default/index.vue
rename to docs/src/demos/Examples/Default/Vue/index.vue
diff --git a/docs/src/demos/React/index.jsx b/docs/src/demos/React/index.jsx
index 021ac371..3e0bd73a 100644
--- a/docs/src/demos/React/index.jsx
+++ b/docs/src/demos/React/index.jsx
@@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { defaultExtensions } from '@tiptap/starter-kit'
-import { useEditor, Editor } from './components/Editor'
+import { useEditor, Editor } from '@tiptap/react'
// Menu bar example component
// useEditor only works for child components of
diff --git a/docs/src/docPages/examples/default.md b/docs/src/docPages/examples/default.md
index 855996c8..47ff89cd 100644
--- a/docs/src/docPages/examples/default.md
+++ b/docs/src/docPages/examples/default.md
@@ -1,4 +1,8 @@
# Default text editor
Did we mention that you have full control over the rendering of the editor? Here is barebones example without any styling, but packed with a whole set of common extensions.
-
+## Vue
+
+
+## React
+
diff --git a/packages/react/README.md b/packages/react/README.md
new file mode 100644
index 00000000..a2dfdd18
--- /dev/null
+++ b/packages/react/README.md
@@ -0,0 +1,14 @@
+# @tiptap/react
+[](https://www.npmjs.com/package/@tiptap/react)
+[](https://npmcharts.com/compare/tiptap?minimal=true)
+[](https://www.npmjs.com/package/@tiptap/react)
+[](https://github.com/sponsors/ueberdosis)
+
+## Introduction
+tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
+
+## Offical Documentation
+Documentation can be found on the [tiptap website](https://tiptap.dev).
+
+## License
+tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
diff --git a/packages/react/package.json b/packages/react/package.json
new file mode 100644
index 00000000..95f31fb4
--- /dev/null
+++ b/packages/react/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@tiptap/react",
+ "description": "React components for tiptap",
+ "version": "2.0.0-alpha.1",
+ "private": true,
+ "homepage": "https://tiptap.dev",
+ "keywords": [
+ "tiptap",
+ "tiptap react components"
+ ],
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "main": "dist/tiptap-react.cjs.js",
+ "umd": "dist/tiptap-react.umd.js",
+ "module": "dist/tiptap-react.esm.js",
+ "unpkg": "dist/tiptap-react.bundle.umd.min.js",
+ "types": "dist/packages/react/src/index.d.ts",
+ "files": [
+ "src",
+ "dist"
+ ],
+ "peerDependencies": {
+ "@tiptap/core": "^2.0.0-alpha.6",
+ "react": "^17.0.1"
+ },
+ "dependencies": {
+ "prosemirror-view": "^1.17.6"
+ }
+}
diff --git a/packages/react/src/ReactNodeViewRenderer.ts b/packages/react/src/ReactNodeViewRenderer.ts
new file mode 100644
index 00000000..7fb1a1a8
--- /dev/null
+++ b/packages/react/src/ReactNodeViewRenderer.ts
@@ -0,0 +1 @@
+export default class ReactNodeViewRenderer {}
diff --git a/packages/react/src/ReactRenderer.ts b/packages/react/src/ReactRenderer.ts
new file mode 100644
index 00000000..d2672c60
--- /dev/null
+++ b/packages/react/src/ReactRenderer.ts
@@ -0,0 +1 @@
+export default class ReactRenderer {}
diff --git a/packages/react/src/components/Editor.jsx b/packages/react/src/components/Editor.jsx
new file mode 100644
index 00000000..8eec95cd
--- /dev/null
+++ b/packages/react/src/components/Editor.jsx
@@ -0,0 +1,34 @@
+import React, {
+ useState, useRef, useEffect, createContext, useContext,
+} from 'react'
+import { Editor as Tiptap } from '@tiptap/core'
+
+export const EditorContext = createContext({})
+
+export const useEditor = () => useContext(EditorContext)
+
+export const Editor = ({
+ value, onChange, children, ...props
+}) => {
+ const [editor, setEditor] = useState(null)
+ const editorRef = useRef(null)
+
+ useEffect(() => {
+ const e = new Tiptap({
+ element: editorRef.current,
+ content: value,
+ ...props,
+ }).on('transaction', () => {
+ onChange(e.getJSON())
+ })
+
+ setEditor(e)
+ }, [])
+
+ return (
+
+ {editorRef.current && children}
+
+
+ )
+}
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
new file mode 100644
index 00000000..2fdac48e
--- /dev/null
+++ b/packages/react/src/index.ts
@@ -0,0 +1,7 @@
+// @ts-nocheck
+export * from '@tiptap/core'
+export { default as ReactRenderer } from './ReactRenderer'
+export { default as ReactNodeViewRenderer } from './ReactNodeViewRenderer'
+export {
+ Editor, EditorContext, useEditor,
+} from './components/Editor'