Edit Images With Vue And Pintura

In this quick tutorial we’ll build a Vue image editor using Pintura a powerful JavaScript image and video editor SDK.

We could build a complete image editor ourselves but that would literally take months, it would turn this quick tutorial into a not-so-quick tutorial.

To get started we’ll set up a basic Vue Composition API based template and then add Pintura.

<script setup>
    // Coming up next...
</script>

<template>
    <!-- File selection -->
    <input type="file" />

    <!-- Editor output -->
    <img src="" alt="" />
</template>

While this tutorial is based on the Vue 3 Composition API, Pintura works just as well with Vue 2 or the Vue 3 template API.

Receiving User Selected Files

Let’s first write the logic get the selected image file from the file input.

We’ll need to get the files, select the first file, and then make sure it’s an image.

<script setup>
    function handleFile(e) {
        // Get files from the file input
        const { files } = e.target;

        // Only need the first file
        const [file] = files;

        // We're only interested in images, also, if no file selected we can't continue
        if (!file || !/image/.test(file.type)) return;

        // We're ready to edit an image...
    }
</script>

<template>
    <input type="file" @change="handleFile" />

    <img src="" alt="" />
</template>

Editing Images With Vue

Now that we’re sure we’ve selected an image file we can add Pintura and edit the image.

We’ll import the openDefaultEditor factory, this creates and opens a feature complete image editor instance in a modal.

<script setup>
    // Import the default Pintura modal editor
    import { openDefaultEditor } from '@pqina/pintura';

    function handleFile(e) {
        const { files } = e.target;

        const [file] = files;

        if (!file || !/image/.test(file.type)) return;

        // Open Pintura in a modal
        const editor = openDefaultEditor({
            src: file,
        });

        // Fires when we're done editing the image
        editor.on('process', ({ dest }) => {
            // Next up is showing the resulting image...
        });
    }
</script>

<template>
    <input type="file" @change="handleFile" />

    <img src="" alt="" />
</template>

Showing The Output Image

When we’re done editing an image the resulting image is returned in the callback assigned to the 'process' event. The dest property contains the output image file.

By defining a reactive property we can set the src attribute of the image by updating the src property of the outputImage object.

<script setup>
    import { openDefaultEditor } from '@pqina/pintura';

    // The Vue reactive property helper
    import { reactive } from 'vue';

    // The source of the image we want to display
    const outputImage = reactive({ src: '' });

    function handleFile(e) {
        const { files } = e.target;

        const [file] = files;

        if (!file || !/image/.test(file.type)) return;

        const editor = openDefaultEditor({
            src: file,
        });

        editor.on('process', ({ dest }) => {
            // Show the resulting image file
            outputImage.src = URL.createObjectURL(dest);
        });
    }
</script>

<template>
    <input type="file" @change="handleFile" />

    <img :src="outputImage.src" alt="" />
</template>

Uploading The Resulting Image

We can use Pintura to upload the edited image as well. We set the store property on the imageWriter, this tells Pintura to POST the resulting image to the specified URL. Pintura will show a progress indicator while it’s busy uploading.

<script setup>
    import { openDefaultEditor } from '@pqina/pintura';

    import { reactive } from 'vue';

    const outputImage = reactive({ src: '' });

    function handleFile(e) {
        const { files } = e.target;

        const [file] = files;

        if (!/image/.test(file.type)) return;

        const editor = openDefaultEditor({
            src: file,

            // Here we pass the `store` property to the `imageWriter`, it'll
            // POST our image as FormData to the defined end point at `/upload/`
            imageWriter: {
                store: '/upload/',
            },
        });

        editor.on('process', ({ dest }) => {
            outputImage.src = URL.createObjectURL(dest);
        });
    }
</script>

<template>
    <input type="file" @change="handleFile" />

    <img :src="outputImage.src" alt="" />
</template>

Conditionally Loading Pintura

Pintura weighs in at about half the size of a 5¼ inch floppy (about 200KB gzipped), while super small for an image editor, that’s still quite a lot of data for slower connections.

To improve page performance we can use tree-shaking and only import the internal Pintura modules we need, but on top of that we can wait with loading Pintura until the user selects an image to edit. It’s at that time we need the editor image editing.

Let’s make this tiny change now, we’ll use a dynamic import to load the module.

<script setup>
    import { reactive } from 'vue';

    const outputImage = reactive({ src: '' });

    async function handleFile(e) {
        const { files } = e.target;

        const [file] = files;

        if (!file || !/image/.test(file.type)) return;

        // Dynamically load Pintura when the user has selected a valid image file
        const { openDefaultEditor } = await import('@pqina/pintura');

        const editor = openDefaultEditor({
            src: file,
            imageWriter: {
                store: '/upload/',
            },
        });

        editor.on('process', ({ dest }) => {
            outputImage.src = URL.createObjectURL(dest);
        });
    }
</script>

<template>
    <input type="file" @change="handleFile" />

    <img :src="outputImage.src" alt="" />
</template>

Conclusion

With only a couple lines of code we’ve added a fully featured image editing solution to our project and we didn’t even use the Vue Pintura Components. While integrating Pintura we’ve learned about reactive Vue objects and dynamic loading of JavaScript modules.

We’ve got everything we need to add image editing functionality to a Vue project, making it possible to crop profile pictures, build photo collages, annotate images, and much more.

I share web dev tips on Twitter, if you found this interesting and want to learn more, follow me there

Or join my newsletter

More articles More articles