Skip to content

useLocalFile

Manage a single locally-uploaded File together with its preview URL. The composable exposes refs for the file and a URL.createObjectURL URL, and takes care of revoking the object URL when the file is replaced or cleared.

Demo

<script setup lang="ts">
    import { useLocalFile } from '@basmilius/common';

    const {file, url, upload, delete: remove} = useLocalFile();

    function onChange(evt: Event): void {
        const picked = (evt.target as HTMLInputElement).files?.[0];

        if (picked) {
            upload(picked);
        }
    }
</script>

<template>
    <div class="demo">
        <input type="file" accept="image/*" @change="onChange"/>

        <div v-if="url" class="preview">
            <img :src="url" alt=""/>
            <div class="meta">
                <p>{{ file?.name }}</p>
                <button @click="remove">Remove</button>
            </div>
        </div>
    </div>
</template>

<style scoped>
    .demo {
        padding: 24px;
        border: 1px solid var(--vp-c-border);
        border-radius: 12px;
        background: var(--vp-c-bg-soft);
    }

    .preview {
        display: flex;
        align-items: center;
        gap: 16px;
        margin-top: 16px;
    }

    .preview img {
        width: 96px;
        height: 96px;
        border-radius: 8px;
        object-fit: cover;
        background: var(--vp-c-bg);
    }

    .meta {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }

    .meta p {
        margin: 0;
        font-family: var(--vp-font-family-mono);
        font-size: 13px;
        color: var(--vp-c-text-2);
    }

    .meta button {
        padding: 4px 12px;
        border: 1px solid var(--vp-c-border);
        border-radius: 8px;
        background: var(--vp-c-bg);
        font-size: 13px;
        color: var(--vp-c-text-1);
    }
</style>

Importing

ts
import { useLocalFile } from '@basmilius/common';

Usage

vue
<script setup lang="ts">
    import { useLocalFile } from '@basmilius/common';

    const {
        file,
        url,
        upload,
        delete: removeFile
    } = useLocalFile();

    function onChange(event: Event): void {
        const input = event.target as HTMLInputElement;
        const next = input.files?.[0];

        if (next) {
            upload(next);
        }
    }
</script>

<template>
    <input type="file" @change="onChange"/>
    <img v-if="url" :src="url" alt=""/>
    <button v-if="file" @click="removeFile">Remove</button>
</template>

Both file and url are null until a file is uploaded. Replacing the file triggers a URL.revokeObjectURL for the previous preview to avoid memory leaks; the watch cleanup handles unmount as well.

Returned bindings

PropertyTypeDescription
fileRef<File | null>Currently uploaded file
urlRef<string | null>Object URL for the file's preview
upload(file)(file: File) => voidSet the file and create a preview URL
delete()() => voidClear the file and revoke the preview URL

Type signature

ts
declare function useLocalFile(): {
    readonly file: Ref<File | null>;
    readonly url: Ref<string | null>;
    delete: () => void;
    upload: (file: File) => void;
};