---
title: "Alpine"
description: "Discover how to use Tiptap with Alpine.js to create a powerful text editor. Follow our detailed guide in our docs!"
canonical_url: "https://tiptap.dev/docs/editor/getting-started/install/alpine"
---

# Alpine

Discover how to use Tiptap with Alpine.js to create a powerful text editor. Follow our detailed guide in our docs!

The following guide describes how to integrate Tiptap with version 3 of Alpine.js. For the sake of this guide, we'll use Vite to quickly set up a project, but you can use whatever you're used to. Vite is just really fast and we love it!

### Requirements

- [Node](https://nodejs.org/en/download/) installed on your machine
- Experience with [Alpine.js](https://github.com/alpinejs/alpine)

## Create a project (optional)

If you already have an Alpine.js project, that's fine too. Just skip this step.

For the purpose of this guide, start with a fresh [Vite](https://vitejs.dev/) project called `my-tiptap-project`. Vite sets up everything we need, just select the Vanilla JavaScript template.

```bash
npm init vite@latest my-tiptap-project -- --template vanilla
cd my-tiptap-project
npm install
npm run dev
```

### Install the dependencies

Okay, enough of the boring boilerplate work. Let's finally install Tiptap! For the following example, you'll need `alpinejs`, the `@tiptap/core` package, the `@tiptap/pm` package, and the `@tiptap/starter-kit`, which includes the most common extensions to get started quickly.

```bash
npm install alpinejs @tiptap/core @tiptap/pm @tiptap/starter-kit
```

If you followed step 1, you can now start your project with `npm run dev`, and open [http://localhost:5173](http://localhost:5173) in your favorite browser. This might be different if you're working with an existing project.

## Integrate Tiptap

To actually start using Tiptap, you'll need to write a little bit of JavaScript. Let's put the following example code in a file called `main.js`.

This is the fastest way to get Tiptap up and running with Alpine.js. It will give you a very basic version of Tiptap. No worries, you will be able to add more functionality soon.

```js
import Alpine from 'alpinejs'
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'

document.addEventListener('alpine:init', () => {
  Alpine.data('editor', (content) => {
    let editor // Alpine's reactive engine automatically wraps component properties in proxy objects. If you attempt to use a proxied editor instance to apply a transaction, it will cause a "Range Error: Applying a mismatched transaction", so be sure to unwrap it using Alpine.raw(), or simply avoid storing your editor as a component property, as shown in this example.

    return {
      updatedAt: Date.now(), // force Alpine to rerender on selection change
      init() {
        const _this = this

        editor = new Editor({
          element: this.$refs.element,
          extensions: [StarterKit],
          content: content,
          onCreate({ editor }) {
            _this.updatedAt = Date.now()
          },
          onUpdate({ editor }) {
            _this.updatedAt = Date.now()
          },
          onSelectionUpdate({ editor }) {
            _this.updatedAt = Date.now()
          },
        })
      },
      isLoaded() {
        return editor
      },
      isActive(type, opts = {}) {
        return editor.isActive(type, opts)
      },
      toggleHeading(opts) {
        editor.chain().toggleHeading(opts).focus().run()
      },
      toggleBold() {
        editor.chain().focus().toggleBold().run()
      },
      toggleItalic() {
        editor.chain().toggleItalic().focus().run()
      },
    }
  })
})

window.Alpine = Alpine
Alpine.start()
```

### Add it to your app

Now, let's replace the contents of `index.html` with the following example code to use the editor in our app.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
  </head>
  <body>
    <div x-data="editor('<p>Hello world! :-)</p>')">
      <template x-if="isLoaded()">
        <div class="menu">
          <button
            @click="toggleHeading({ level: 1 })"
            :class="{ 'is-active': isActive('heading', { level: 1 }, updatedAt) }"
          >
            H1
          </button>
          <button @click="toggleBold()" :class="{ 'is-active' : isActive('bold', updatedAt) }">
            Bold
          </button>
          <button @click="toggleItalic()" :class="{ 'is-active' : isActive('italic', updatedAt) }">
            Italic
          </button>
        </div>
      </template>

      <div x-ref="element"></div>
    </div>

    <script type="module" src="/main.js"></script>

    <style>
      body {
        margin: 2rem;
        font-family: sans-serif;
      }
      button.is-active {
        background: black;
        color: white;
      }
      .tiptap {
        padding: 0.5rem 1rem;
        margin: 1rem 0;
        border: 1px solid #ccc;
      }
    </style>
  </body>
</html>
```

Tiptap should now be visible in your browser. Time to give yourself a pat on the back! :)

## Next steps

- [Configure your editor](https://tiptap.dev/docs/editor/getting-started/configure.md)
- [Add styles to your editor](https://tiptap.dev/docs/editor/getting-started/style-editor.md)
- [Learn more about Tiptaps concepts](https://tiptap.dev/docs/editor/core-concepts/introduction.md)
- [Learn how to persist the editor state](https://tiptap.dev/docs/editor/core-concepts/persistence.md)
- [Start building your own extensions](https://tiptap.dev/docs/editor/extensions/custom-extensions.md)
