3 min read

Drawer Files Component

Skip the headache and make file upload/selection easier with this powerful native component from Directus.

The v-drawer-files component is a drawer from Directus that allows users to browse and select files from the directus_filescollection with folder navigation capabilities. This component is natively available to extensions and doesn't need to be imported.

Key Features:

  • Browse files organized in folders
  • Select single or multiple files
  • Context-aware filtering based on current folder and special folders
  • Support for access control integration with user permissions

Component API

Props

The following properties are available:

  • collection - The collection to browse files from. Defaults to Directus' built-in directus_files collection. You can specify a custom collection if needed.
  • folder - Sets the root folder ID that constrains browsing. If set, Users can only navigate within this folder and its subfolders.
  • field - A unique identifier (typically the field name) used to scope the component's session storage. This allows multiple instances of the component on the same page to maintain independent folder states. For example, using field="avatar" and field="banner" on the same page keeps their navigation states separate. Normally this will be the field key of the current interface but layouts can use custom strings depending on the use case.
  • filter - Additional Directus filters to apply to the file list. These are merged with the component's internal folder filtering logic. This can be useful if you need to limit what files can be seen, such and images or documents.

How It Works

Basic Usage

Here's how to use the v-drawer-files component in your extension:

<script setup lang="ts">
import { ref } from 'vue';

// Track whether the drawer is open
const showFileDrawer = ref(false);

// Handle selected files
const handleFileSelect = (files: any[]) => {
  console.log('Selected files:', files);
  // Process selected files here
};
</script>

<template>
  <div>
    <!-- Button to open the file drawer -->
    <button @click="showFileDrawer = true">
      Select Files
    </button>

    <!-- File drawer component -->
    <v-drawer-files
      v-model:active="showFileDrawer"
      collection="directus_files"
      @input="handleFileSelect"
    />
  </div>
</template>

Advanced Usage Example

Here's a more advanced example with custom filtering and field scoping:

<script setup lang="ts">
import { ref } from 'vue';
import { Filter } from '@directus/types';

const showAvatarDrawer = ref(false);
const showBannerDrawer = ref(false);
const selectedAvatar = ref<any>(null);
const selectedBanner = ref<any>(null);

// Optional: Filter to only show image files
const imageFilter: Filter = {
  type: {
    _in: ['image/jpeg', 'image/png', 'image/webp'],
  },
};

const handleAvatarSelect = (files: any[]) => {
  if (files.length > 0) {
    selectedAvatar.value = files[0]; // Single select
  }
};

const handleBannerSelect = (files: any[]) => {
  if (files.length > 0) {
    selectedBanner.value = files[0];
  }
};
</script>

<template>
  <div>
    <!-- Avatar file selector -->
    <div>
      <button @click="showAvatarDrawer = true">
        Select Avatar
      </button>
      <v-drawer-files
        v-model:active="showAvatarDrawer"
        field="avatar"
        folder="<avatars-folder-id>"
        :filter="imageFilter"
        @input="handleAvatarSelect"
      />
      <div v-if="selectedAvatar">
        Selected: {{ selectedAvatar.title }}
      </div>
    </div>

    <!-- Banner file selector -->
    <div>
      <button @click="showBannerDrawer = true">
        Select Banner
      </button>
      <v-drawer-files
        v-model:active="showBannerDrawer"
        field="banner"
        folder="<banners-folder-id>"
        :filter="imageFilter"
        @input="handleBannerSelect"
      />
      <div v-if="selectedBanner">
        Selected: {{ selectedBanner.title }}
      </div>
    </div>
  </div>
</template>

State Management

The component uses Vue's useSessionStorage composable to persist the user's navigation state during their session:

  • Scoped by context: Each combination of collectionfield, and folder gets its own storage key, preventing state collisions.
  • Automatic persistence: When users navigate to different folders, the state is automatically saved.
  • Session-based: State persists across component remounting but clears when the browser session ends.

Events

The component emits an input event when files are selected:

TypeScript

@input="(files: any[]) => void"

The event payload is an array of selected file objects, each containing properties like idtitletype, and modified_on.

Practical Use Cases

1. User Profile Avatar Selection:

<v-drawer-files
  v-model:active="selectingAvatar"
  field="avatar"
  @input="(files) => user.avatar_id = files[0].id"
/>

2. Multiple File Selection Dialog:

<v-drawer-files
  v-model:active="selectingDocuments"
  field="documents"
  multiple
  @input="(files) => documents.push(...files)"
/>

Send me a coffee