CodeMirror v6 on Vue3 hooked to Pinia store
Tags: codemirror, vue, pinia
Published: 2023-07-30 20:50:32

## Scope

I'm gonna make a very basic Codepen clone for writing HTML and CSS, and integrate CodeMirror in Vue using `vue-codemirror` wrapper.

## Setup

After setting up Vue with Pinia, install the following packages:

```shell
npm i vue-codemirror @codemirror/lang-css @codemirror/lang-html @codemirror/theme-one-dark
```

## Code

### 1. The store

```js
import { defineStore } from "pinia";

import { computed, ref } from "vue";

export const useAppStore = defineStore("app", () => {
    const css = ref("");
    const cssCopy = computed(() => css.value);
    const setCss = (newCss) => {
        css.value = newCss;
    };

    const template = ref("");
    const templateCopy = computed(() => template.value);
    const setTemplate = (newTemplate) => {
        template.value = newTemplate;
    };

    return {
        css,
        cssCopy,
        setCss,
        template,
        templateCopy,
        setTemplate,
    };
});
```

This is the store I made to keep track of the value of HTML and CSS code.

### 2. Base text editor component

```vue
<template>
    <codemirror
        :modelValue="props.code"
        placeholder="code goes here..."
        :style="{ height: '100%' }"
        :autofocus="true"
        :indent-with-tab="true"
        :tab-size="4"
        :extensions="extensions"
        @ready="handleReady"
    />
</template>

<script setup>
import { shallowRef, computed } from "vue";
import { Codemirror } from "vue-codemirror";
import { oneDark } from "@codemirror/theme-one-dark";

const props = defineProps({
    additionalExtensions: {
        type: Array,
        required: true,
        default: [],
    },
    code: {
        type: String,
        required: true,
    },
});

const baseExtensions = [oneDark];

const extensions = computed(() => [
    ...baseExtensions,
    ...props.additionalExtensions,
]);

const view = shallowRef();
const handleReady = (payload) => {
    view.value = payload.view;
};
</script>
```

This component defines two simple properties:

1. `additionalExtensions` mainly to add language support.
2. `code` to access value from store.

### 3. Language specific text editors

```vue
<template>
    <base-editor
        :additional-extensions="[html()]"
        :code="store.templateCopy"
        @change="store.setTemplate($event)"
    />
</template>

<script setup>
import { html } from "@codemirror/lang-html";

import { useAppStore } from "@/stores/app";
import BaseEditor from "@/components/BaseEditor.vue";

const store = useAppStore();

store.setTemplate("<h1>template</h1>");
</script>
```

```vue
<template>
    <base-editor
        :additionalExtensions="[css()]"
        :code="store.cssCopy"
        @change="store.setCss($event)"
    />
</template>

<script setup>
import { css } from "@codemirror/lang-css";

import { useAppStore } from "@/stores/app";
import BaseEditor from "@/components/BaseEditor.vue";

const store = useAppStore();

store.setCss(`* {
    padding = 0;
    margin = 0;
}`);
</script>
```

### 4. Putting It All Together

```vue
<template>
    <div class="grid h-screen grid-cols-8 overflow-hidden bg-slate-800">
        <div class="h-screen col-span-4 overflow-hidden border border-gray-300">
            <div v-html="compiledCode"></div>
        </div>
        <div
            class="flex flex-col col-span-4 overflow-hidden border border-gray-300 grow-0 shrink basis-full"
        >
            <div
                class="overflow-hidden border border-gray-300 grow-0 shrink basis-1/2"
            >
                <div class="h-full overflow-auto">
                    <HtmlEditor />
                </div>
            </div>
            <div
                class="overflow-hidden border border-gray-300 grow-0 shrink basis-1/2"
            >
                <div class="h-full overflow-auto">
                    <CssEditor />
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { computed } from "vue";
import HtmlEditor from "@/components/HtmlEditor.vue";
import CssEditor from "@/components/CssEditor.vue";

import { useAppStore } from "@/stores/app";

const store = useAppStore();

const compiledCode = computed(() => {
    return `
        <style>${store.css}</style>
        ${store.template}
    `;
});
</script>
```

## Results


![screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3h3oylcygzn9gm3709o.png)