npx skills add ...
npx skills add tigrisdata/skills --skill file-storage
Use when working with Tigris file storage - uploading, downloading, deleting, listing files, presigned URLs, client uploads, or setting up Tigris CLI and SDK. Covers Next.js, Remix, Express, Rails, and Laravel. For Python/Django, see the tigris-python-sdk skill.
npx skills add tigrisdata/skills --skill file-storage
Store and serve files with Tigris Object Storage. Covers CLI setup (bucket, access keys, auth) and the @tigrisdata/storage SDK for application code.
See Getting Started with CLI below for detailed steps.
Verify the installation:
t3 is an alias for tigris — all commands work with either.
Opens browser for OAuth. After login, verify with:
For CI/CD or non-interactive environments:
Key points:
--public for publicly readable objects.--locations to pin to specific regions.help after any command to see its options (e.g., tigris buckets create help).This outputs an Access Key ID (tid_xxx) and Secret Access Key (tsec_yyy).
The Secret Access Key is only shown once. Copy it immediately. The Name field is only for human identification — it has no functional impact.
Create .env in your project root:
TIGRIS_STORAGE_BUCKET sets the default bucket for all SDK calls. Add .env to .gitignore — never commit credentials.
Roles:
| Role | Permissions | Use when |
|---|---|---|
Editor | Read + write + delete objects | App servers that upload/delete files |
ReadOnly | Read objects only | Apps that only serve/download files |
Now you have:
my-app-uploads)tid_xxx / tsec_yyy).env file ready for the SDKSupports ES Modules and CommonJS.
All methods return TigrisStorageResponse<T, E>. Always check error first:
Every method accepts an optional config parameter of type TigrisStorageConfig:
Use config to target a different bucket or use different credentials per call:
Put options:
| Option | Values | Default | Purpose |
|---|---|---|---|
| access | public, private | private | Object visibility |
| addRandomSuffix | boolean | false | Append random suffix to avoid collisions on user-uploaded files with the same name |
| allowOverwrite | boolean | true | Allow replacing existing file |
| contentType | MIME string | inferred | Content type header |
| contentDisposition | inline,attachment | inline | Browser display behavior |
| multipart | boolean | false | Enable for large files |
| onUploadProgress | callback | — | {loaded, total, percentage} |
| config | TigrisStorageConfig | — | Override bucket/credentials (see config section above) |
Response data: { url, path, size, contentType, contentDisposition, modified }
Get options:
| Option | Values | Default | Purpose |
|---|---|---|---|
| contentDisposition | inline,attachment | inline | Display vs download |
| contentType | MIME string | from upload | Override content type |
| encoding | string | utf-8 | Text encoding |
| config | TigrisStorageConfig | — | Override bucket/credentials (see config section above) |
List options:
| Option | Purpose |
|---|---|
| prefix | Filter keys starting with this string |
| delimiter | Group keys (e.g., "/" for folders) |
| limit | Max objects per page (default: 100) |
| paginationToken | Continue from previous page |
| config | TigrisStorageConfig — override bucket/credentials (see config section above) |
Response data: { items, paginationToken, hasMore }
Presigned URL options:
| Option | Values | Default | Purpose |
|---|---|---|---|
| operation | get,put | — | URL purpose |
| expiresIn | seconds | 3600 | Expiration time |
| contentType | MIME string | — | Required for PUT |
| config | TigrisStorageConfig | — | Override bucket/credentials (see config section above) |
Response data: { url, method, expiresIn }
Upload files directly from the browser to Tigris without routing bytes through your server. Uses presigned URLs under the hood.
Client upload options:
| Option | Required | Purpose |
|---|---|---|
| url | Yes | Backend endpoint for presigned URLs |
| access | No | public or private (default) |
| multipart | No | Enable for large files |
| partSize | No | Bytes per part (default: 5 MiB) |
| concurrency | No | Parallel part uploads (default: 4) |
| contentType | No | MIME type |
| onUploadProgress | No | {loaded, total, percentage} |
npm install @tigrisdata/react provides a drop-in <Uploader> component with file selection, progress, and error handling built in. See @tigrisdata/react docs for usage.
Always: Check result.error before result.data | Upload files as private by default — only set access: "public" when anonymous users need direct URL access | Use handleClientUpload for browser uploads (don't route bytes through server) | Use multipart: true for files over 100MB | Paginate list() with hasMore + paginationToken | Delete old files when replacing (no auto-cleanup) | Set contentType explicitly when it matters
Never: Expose access keys to the client (use handleClientUpload + upload() from @tigrisdata/storage/client) | Skip error checking | Use generic paths like file.jpg (use avatars/${userId}.jpg or timestamps) | Forget to save the Secret Access Key on creation (shown only once)
| Problem | Cause & Fix |
|---|---|
| "Access denied" on upload | Key not assigned to bucket. Run tigris access-keys assign tid_xxx --bucket <name> --role Editor |
| "Bucket not found" from SDK | Wrong bucket name in .env. Verify with tigris buckets list |
| Secret Access Key lost | Cannot recover. Create new: tigris access-keys create "new-key" and reassign |
| Files not publicly accessible | Bucket is private by default. Use --public flag or access: "public" on put() |
| Upload hangs on large files | Add multipart: true to put options for files over 100MB |
| List returns incomplete results | Default limit is 100. Use hasMore + paginationToken to paginate |
| Client upload fails (CORS/500) | Server route must use handleClientUpload from @tigrisdata/storage |
t3 is an alias for tigris. Type help after any command for options.
Remote paths use t3:// prefix: t3://my-bucket/path/file.txt
For framework-specific upload/download patterns, read the resource file for your framework:
| Framework | SDK | Resource |
|---|---|---|
| Next.js | @tigrisdata/storage (native) | Read ./resources/nextjs.md — Server Actions, API Routes, next/image, client uploads |
| Remix | @tigrisdata/storage (native) | Read ./resources/remix.md — action functions, loaders, client uploads |
| Express | @tigrisdata/storage (native) | Read ./resources/express.md — Multer, streaming uploads, client uploads |
| Rails | aws-sdk-s3 (no native Ruby SDK yet) | Read ./resources/rails.md — Active Storage, direct uploads, image variants |
| Django | tigris-boto3-ext + django-storages | Use the tigris-python-sdk skill — covers FileField, django-storages, presigned URLs |
| Laravel | league/flysystem-aws-s3-v3 (no native PHP SDK yet) | Read ./resources/laravel.md — Storage facade, Livewire uploads, presigned URLs |
| Framework | Platform | Set env vars with |
|---|---|---|
| Next.js | Vercel | Dashboard → Settings → Environment Variables |
| Remix | Fly.io | fly secrets set TIGRIS_STORAGE_ACCESS_KEY_ID=... ... |
| Express | Docker | -e flags or .env in Compose |
| Rails | Fly.io / Kamal | fly secrets set or kamal env push |
| Laravel | Forge / Vapor | Dashboard → Environment or vapor env:pull |