Setting up Image Editing with the Input element

The <image-input> custom element is a tiny wrapper around the <input type="file"> element.

It's the easiest and fastest way to add image editing and uploading functionality to a webpage.

<!DOCTYPE html>

<link rel="stylesheet" href="image-input.css" />

<form method="post" action="/upload" enctype="multipart/form-data">
    <image-input name="my-image"></image-input>
    <button type="submit">Upload</button>
</form>

<script src="image-input.js"></script>

This will automatically create a <input type="file" name="my-image"> element with an accept attribute that filters for images and a name attribute set to my-field.

When an image is selected the editor will automatically open in a modal. The file input value is automatically set to the edited file.

Posting files along with the form submit works on browsers that support the DataTransfer constructor, currently this is Firefox, Chrome, Chromium powered browsers, and Safari 14.1.

If we need to support more browsers we can upload images asynchronously using the store attribute.

Configuration

All serializable image editor properties can be assigned to the image-input field.

<image-input image-crop-aspect-ratio="4/3"></image-input>

This also works:

<image-input utils='["crop", "finetune", "filter"]'></image-input>

If we have multiple fields on the page, we can set a global ImageInput property on the window to share configuration options between fields.

<script>
    window.ImageInput = {
        imageCropMinSize: {
            width: 256,
            height: 256,
        },
    };
</script>

<image-input name="my-field"></image-input>

To supply custom properties to a single field we can set the id attribute on our field.

<script>
    // These properties will be applied to all <image-input> elements
    window.ImageInput = {
        imageCropMinSize: {
            width: 256,
            height: 256,
        },
    };

    // These properties will only be assigned to the <image-input id="myField"/> element
    window.myField = {
        imageCropAspectRatio: 1,
    };
</script>

<image-input id="myField" name="my-field"></image-input>

To supply custom options to the imageWriter, for example to resize the output image, we can set the imageWriter property like this.

<script>
    window.ImageInput = {
        imageCropMinSize: {
            width: 256,
            height: 256,
        },
        imageWriter: {
            targetSize: {
                width: 128,
                height: 128,
            },
        },
    };
</script>

<image-input name="my-field"></image-input>

If we add a file input element to the image-input element it'll use that element as the file field. If the accept attribute is omitted or doesn't filter images it's automatically added to the field.

<image-input>
    <input type="file" name="my-field" />
</image-input>

Properties

The <image-input> field accepts the default editor properties and the following list of additional properties.

Attribute Default value Description
name
undefined
Used as name attribute on the internal file input element.
accept
'image/*'
Used as accept attribute on the internal file input element.
capture
undefined
Used as capture attribute on the internal file input element.
src
undefined
Used to set the initial image source.
store
undefined
Optional URL location of the upload handler for async file uploads.
post-field-name
undefined
Custom fields to add to the store upload action. FieldName is the name used in the form post.
input-store-success
'{filename} uploaded successfully'
The label to use for the success message.
input-button-reset
'&times;'
The label to use on the input reset button. Defaults to the × symbol.

store

Set to a target URL and the editor will upload files to that URL when the editing has completed.

If the store property is omitted the output file will be stored in the file field value. At the time of this writing this doesn't work on Safari versions below 14.1.

post-field-name

If store is not set but post fields have been defined the store value is set to an empty string.

The example below will add a field called "action" with value "upload" to the FormData.

<image-input post-custom-data="abcd"></image-input>

Is equal to a field posted like this:

<input type="hidden" name="custom-data" value="abcd" />

or a field added like this:

const fd = FormData();
fd.append('custom-data', 'abcd');

The attribute name without the post- part is used as the field name.

'post-custom-data' -> 'custom-data'
'post-customData' -> 'customData'
'post-custom_data' -> 'custom_data'

Templating

To improve the visual looks and default interaction of the image input element we can supply an HTML template.

The template can contain three optional views which we can mark with the data-empty, data-load, and data-process attributes. There can be one of each.

We're free to use any HTML elements and styles that we like, we only need to add data attributes to tell the image input control how to interact with the template.

<image-input>
    <template>
        <!-- show this if no file loaded -->
        <div data-empty data-drop>
            <p>
                Drag &amp; Drop your image here or
                <button type="button" data-browse>Browse</button>
            </p>
        </div>
        <!-- show this if an image is already loaded. Use `src` attribute 
        on image-input for original image source -->
        <div data-load>
            <img src="image-thumb.jpeg" width="300" />
            <button type="button" data-remove>remove</button>
            <button type="button" data-edit>edit</button>
        </div>
        <!-- show this if an image has been processed -->
        <output data-process>
            <img src="{url}" />
            <span>{filename} uploaded successfully</span>
            <button type="button" data-remove>remove</button>
            <button type="button" data-edit>edit</button>
        </output>
    </template>
</image-input>

The elements in the templates can be decorated with a list of attributes, these attribute can be used to quickly assign behavior to your elements.

Attribute Description
data-drop Element will be able to catch dropped files.
data-browse When element clicked it will open the browse file dialog.
data-remove When element clicked it will trigger removal of current image.
data-reset When element clicked it will reset the input to the load state.
data-edit When element clicked it will open the editor.

The following placeholders in the data-process template are automatically replaced when the template is rendered.

Placeholder Description
{filename} Will be replaced with the filename of the output image.
{url} Will be replace with the object URL of the output image.
{response} Will be replaced with the response of the server if store was set.

Styles

By default no styles are applied to the templates, they're plain HTML. Either style the templates using your favorite CSS framework or use the styles below for a starting point.

/* The empty state grey box */
image-input [data-empty] {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #eee;
}

/* The "drag n drop" text */
image-input [data-empty] p {
    margin: 1em;
    user-select: none;
    text-align: center;
    color: #333;
}

/* To make the browse button blend in with the text */
image-input [data-browse] {
    border: none;
    background: none;
    font: inherit;
    font-size: inherit;
    color: inherit;
    padding: 0;
    margin: 0;
    text-decoration: underline;
    cursor: pointer;
}

/* To make each state equal size and round the corners */
image-input [data-empty],
image-input [data-load] img,
image-input [data-process] img {
    width: 300px;
    height: 225px;
    border-radius: 1rem;
    object-fit: cover;
}

Next steps

With the editor set up, we can continue to configure the editor to our liking by adjusting the available configuration options