Storage
Upload, view, and download files from Appwrite storage buckets.
Appwrite Configuration
In your Appwrite project's Storage dashboard, create a bucket.
Within a bucket you can see its files and configure settings like allowed file types, maximum file size, encryption, and antivirus scanning.
Register Storage Buckets
Register storage buckets in your manifest.json under the data property, same as local data files and database tables. Include the appwriteBucketId value provided by Appwrite.
manifest.json {
"data": {
"assets": {
"appwriteBucketId": "your-bucket-id"
}
},
"appwrite": {
"projectId": "your-project-id",
"endpoint": "your-API-endpoint"
}
}
In this example, the "assets" data source name is arbitrary, used to later reference bucket content in the frontend. An Appwrite data source will automatically reference the appwrite property for credentials.
Alternatively, credentials can be added directly to a data source:
manifest.json {
"data": {
"assets": {
"projectId": "your-project-id",
"endpoint": "your-API-endpoint",
"appwriteBucketId": "your-bucket-id"
}
}
}
Scope
Scoping automatically filters file access by user or team, ensuring users only see files they're permitted to access. Configure scope in manifest.json:
manifest.json {
"data": {
"assets": {
"appwriteBucketId": "your-bucket-id",
"scope": "user"
}
}
}
Scope options are:
| Scope | Description | Permission Used |
|---|---|---|
"user" |
Single user's files | User permissions |
"team" |
Current team's files | Team permissions |
"teams" |
All teams user belongs to | Team permissions |
["user", "team"] |
User's files OR current team's files | User OR team permissions |
["user", "teams"] |
User's files OR any team's files | User OR team permissions |
Scope filters are applied when listing files, ensuring users only see files they have permission to access based on Appwrite's permission system.
When using ["user", "team"] scope, files are shown for the current user OR the current team. If no team is selected ($auth.currentTeam), only user-scoped files will display. Use "teams" (plural) instead of "team" (singular) to query all teams the user belongs to, not just the current team.
Display & Manage Files
Storage buckets work identically to local data sources and database tables. Access files using the $x magic method and perform all CRUD operations:
<div>
<!-- Upload file -->
<label role="button">
<input type="file" @change="if ($event.target.files.length > 0) { $x.assets.$create($event.target.files[0]).then(() => { $event.target.value = ''; }); }" accept="*/*" />
<span>Upload File</span>
</label>
<!-- List files -->
<template x-for="file in $x.assets" :key="file.$id">
<div class="row gap-2 items-center">
<span x-text="file.name"></span>
<button @click="$x.assets.$openUrl(file.$id)" x-icon="lucide:external-link" aria-label="View"></button>
<button @click="$x.assets.$duplicate(file.$id)" x-icon="lucide:copy" aria-label="Duplicate"></button>
<button @click="$x.assets.$delete(file.$id)" x-icon="lucide:trash" aria-label="Delete"></button>
</div>
</template>
</div>
Storage buckets automatically sync changes in realtime across all active sessions. When one user uploads or deletes a file, all other users see the change immediately without page refresh.
Method details:
| Method | Parameters | Description |
|---|---|---|
$create(file, fileId?, permissions?, onProgress | options?) |
file (File object), fileId (string, optional), permissions (array, optional). The 4th argument is either an onProgress callback function or an options object { entryId, table, fileIdsColumn? } for linking the file to another table's entry. |
Upload a new file. Returns uploaded file. If scope is configured, permissions are automatically set based on user/team |
$duplicate(fileId, options?) |
fileId (string or object with $id), options (object, optional) |
Duplicate a file. Options: newName, newFileId. Returns duplicated file. If newName is not provided, the file name will be "{originalName} copy" |
$delete(fileIdOrArray) |
fileIdOrArray (string, object with $id, or array) |
Delete file(s). Returns deleted file(s) |
$url(fileId, token?) |
fileId (string or object with $id), token (string, optional) |
Get file view URL. Returns URL string |
$download(fileId, token?) |
fileId (string or object with $id), token (string, optional) |
Get file download URL. Returns URL string |
$preview(fileId, options?, token?) |
fileId (string or object with $id), options (object, optional), token (string, optional) |
Get file preview URL (images only). Options: width, height, quality, output, etc. Returns preview URL string |
$openUrl(fileId, token?) |
fileId (string or object with $id), token (string, optional) |
Open file view URL in new tab |
$openDownload(fileId, filename?, token?) |
fileId (string or object with $id), filename (string, optional), token (string, optional) |
Open file download URL in new tab |
$openPreview(fileId, options?, token?) |
fileId (string or object with $id), options (object, optional), token (string, optional) |
Open file preview URL in new tab |
Link Files to Database Entries
Files can be automatically linked to database entries when uploaded. Configure the relationship in manifest.json:
manifest.json {
"data": {
"assets": {
"appwriteBucketId": "your-bucket-id",
"belongsTo": {
"table": "projects",
"id": "$entryId",
"fileIdsColumn": "fileIds"
}
},
"projects": {
"appwriteTableId": "your-table-id",
"storage": {
"assets": "fileIds"
}
}
}
}
When a file is uploaded with an entryId parameter, it's automatically added to that entry's fileIds array:
<!-- Upload file and link to project entry -->
<button @click="$x.assets.$create(file, null, null, { entryId: project.$id, table: 'projects' })">
Upload to Project
</button>
Alternatively, link files manually after upload:
<!-- Link file to entry after upload -->
<button @click="
$x.assets.$create(file).then(uploadedFile => {
const currentFileIds = project.fileIds || [];
$x.projects.$update(project.$id, {
fileIds: [...currentFileIds, uploadedFile.$id]
});
});
">
Upload and Link
</button>
See databases for more details on file references.
Properties & Methods
File Properties
Each file includes standard Appwrite properties plus computed properties:
| Property | Type | Description |
|---|---|---|
$id |
string | Unique identifier |
name |
string | File name |
size |
number | File size in bytes |
$formattedSize |
string | Human-readable size (e.g., "2.5 MB") |
mimeType |
string | MIME type (e.g., "image/png") |
$createdAt |
string | Upload timestamp (ISO format) |
$updatedAt |
string | Last update timestamp (ISO format) |
$url |
string | View URL |
$isImage |
boolean | true if image file |
$thumbnailUrl |
string | Thumbnail URL (images only) |
<!-- Access file properties -->
<div x-text="file.$id"></div> <!-- Unique identifier -->
<div x-text="file.name"></div> <!-- File name -->
<div x-text="file.size"></div> <!-- File size in bytes -->
<div x-text="file.$formattedSize"></div> <!-- Human-readable size (e.g., "2.5 MB") -->
<div x-text="file.mimeType"></div> <!-- MIME type (e.g., "image/png") -->
<div x-text="file.$createdAt"></div> <!-- Upload timestamp (ISO) -->
<div x-text="file.$updatedAt"></div> <!-- Last update timestamp (ISO) -->
<div x-text="file.$url"></div> <!-- View URL -->
<div x-text="file.$isImage"></div> <!-- Boolean: true if image file -->
<div x-text="file.$thumbnailUrl"></div> <!-- Thumbnail URL (images only) -->
State Properties
Check data source loading state, errors, and readiness:
| Property | Type | Description |
|---|---|---|
$loading |
boolean | Indicates if data is currently being loaded |
$error |
string | null | Error message if an operation failed (null if no error) |
$ready |
boolean | Indicates if data has been loaded at least once |
<!-- Loading state -->
<small x-show="$x.assets.$loading">Loading files...</small>
<!-- Error state -->
<small x-show="$x.assets.$error" x-text="$x.assets.$error" class="text-error"></small>
<!-- Ready state -->
<small x-show="$x.assets.$ready && !$x.assets.$loading">
Files loaded: <b x-text="$x.assets.length"></b>
</small>
CRUD Methods
<!-- Upload single file -->
<label role="button">
<input type="file" @change="if ($event.target.files.length > 0) { $x.assets.$create($event.target.files[0]); }" accept="*/*" />
<span>Upload</span>
</label>
<!-- Upload with custom file ID -->
<label role="button">
<input type="file" @change="if ($event.target.files.length > 0) { $x.assets.$create($event.target.files[0], 'custom-id'); }" accept="*/*" />
<span>Upload with ID</span>
</label>
<!-- Upload with progress callback -->
<label role="button">
<input type="file" @change="if ($event.target.files.length > 0) {
$x.assets.$create($event.target.files[0], null, null, (progress) => {
console.log('Progress:', progress + '%');
});
}" accept="*/*" />
<span>Upload with Progress</span>
</label>
View & Download Methods
<!-- Get view URL -->
<button @click="const url = await $x.assets.$url(file.$id); console.log(url);">
Get View URL
</button>
<!-- Get view URL with token -->
<button @click="const url = await $x.assets.$url(file.$id, token); console.log(url);">
Get URL with Token
</button>
See the Method details table near the top of this article for the full signatures of $url, $download, $preview, and their $open* variants.
Image Handling
Image files receive special handling with automatic thumbnail generation and preview support:
<!-- Display image with thumbnail -->
<template x-for="file in $x.assets" :key="file.$id">
<div x-show="file.$isImage">
<img :src="file.$thumbnailUrl" :alt="file.name" />
<p x-text="file.name"></p>
</div>
</template>
<!-- Display full-size image -->
<img :src="file.$url" :alt="file.name" />
<!-- Preview with custom dimensions -->
<button @click="$x.assets.$openPreview(file.$id, {
width: 1200,
height: 800,
quality: 95,
output: 'webp'
})">
High Quality Preview
</button>
Image preview options:
| Option | Type | Description |
|---|---|---|
width |
integer | Resize width (0-4000) |
height |
integer | Resize height (0-4000) |
quality |
integer | Image quality (0-100) |
output |
string | Format: jpeg, jpg, png, gif, webp |
gravity |
string | Crop gravity: center, top-left, top, top-right, left, right, bottom-left, bottom, bottom-right |
borderWidth |
integer | Border width in pixels (0-100) |
borderColor |
string | Border color (HEX without #) |
borderRadius |
integer | Border radius in pixels (0-4000) |
opacity |
number | Opacity (0-1, PNG only) |
rotation |
integer | Rotation in degrees (-360 to 360) |
background |
string | Background color (HEX without #, PNG only) |
Next Steps
See databases for managing database entries and linking files to entries.
Article does not exist
There is no documentation at this path.