PHPackages                             jegex/laravel-media - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. jegex/laravel-media

ActiveLibrary[File &amp; Storage](/categories/file-storage)

jegex/laravel-media
===================

Laravel media library — upload, image conversions, responsive images, video thumbnails, ZIP export, and more

v1.0.0-alpha.5(1mo ago)06[1 PRs](https://github.com/jegex/laravel-media/pulls)MITPHPPHP ^8.4CI passing

Since May 8Pushed 3w agoCompare

[ Source](https://github.com/jegex/laravel-media)[ Packagist](https://packagist.org/packages/jegex/laravel-media)[ Docs](https://github.com/jegex/laravel-media)[ GitHub Sponsors](https://github.com/jegex)[ RSS](/packages/jegex-laravel-media/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (5)Dependencies (16)Versions (7)Used By (0)

jegex/laravel-media
===================

[](#jegexlaravel-media)

[![Latest Version on Packagist](https://camo.githubusercontent.com/86287e06949c6d8e331942f4821979b126ee8a343b0fd91a8f0ce0355bef94c1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a656765782f6c61726176656c2d6d656469612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/jegex/laravel-media)[![Tests](https://github.com/jegex/laravel-media/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jegex/laravel-media/actions/workflows/run-tests.yml)[![Total Downloads](https://camo.githubusercontent.com/e5f0f0b88cd44e574cb99de6ce90d5b3ae2033ab1ba24bbbaa2d29f3fe8b3d10/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6a656765782f6c61726176656c2d6d656469612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/jegex/laravel-media)[![PHP Version](https://camo.githubusercontent.com/02463ad42fbbb8e930dc93f83e8b2ecd9ad3f718d33bb429f5f8f792f9cfd2e5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e342d626c75652e737667)](https://php.net)[![Laravel Version](https://camo.githubusercontent.com/db113744db490abfea4d77cd2ac18f46e44008573ab73c8ffe74fcf8f9e216c8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d3131253230253743253230313225323025374325323031332d7265642e737667)](https://laravel.com)

Powerful media management package for Laravel applications. Handle file uploads, image conversions, responsive images, and video thumbnails with ease. Inspired by `spatie/laravel-medialibrary`.

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Usage](#usage)
    - [Adding Media](#adding-media)
    - [Retrieving Media](#retrieving-media)
    - [Media Properties](#media-properties)
    - [Rendering Media](#rendering-media)
- [Image Conversions](#image-conversions)
- [Responsive Images](#responsive-images)
- [Queue System](#queue-system)
- [Vapor Uploads](#vapor-uploads)
- [ZIP Export](#zip-export)
- [Facade Usage](#facade-usage)
- [Configuration](#configuration)
- [Environment Variables](#environment-variables)
- [Advanced Usage](#advanced-usage)
- [Testing](#testing)
- [Changelog](#changelog)
- [License](#license)

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require jegex/laravel-media
```

### Post-Installation Steps

[](#post-installation-steps)

#### Step 1: Publish Configuration &amp; Migration

[](#step-1-publish-configuration--migration)

```
# Publish config file to config/media.php
php artisan vendor:publish --tag="media-config"

# Publish migration file
php artisan vendor:publish --tag="media-migrations"
```

#### Step 2: Run Migration

[](#step-2-run-migration)

```
php artisan migrate
```

#### Step 4: Configure Storage Disk (Optional)

[](#step-4-configure-storage-disk-optional)

In `config/media.php` or your `.env` file, set the default storage disk:

```
MEDIA_DISK=public
```

Then create the symbolic link for public disk:

```
php artisan storage:link
```

#### Step 5: Configure Queue (Optional)

[](#step-5-configure-queue-optional)

If you want image conversions to run asynchronously:

```
QUEUE_CONNECTION=redis
QUEUE_CONVERSIONS_BY_DEFAULT=true
QUEUE_CONVERSIONS_AFTER_DB_COMMIT=true
```

Quick Start
-----------

[](#quick-start)

Add the `HasMedia` trait to your model:

```
use Jegex\Media\MediaCollections\Concerns\HasMedia;

class Post extends Model
{
    use HasMedia;

    // ...
}
```

Now you can attach media:

```
$post = Post::create(['title' => 'My Post']);

$post->addMedia('/path/to/image.jpg')
    ->toMediaCollection('images');

$post->getMedia('images')->first()->getUrl();
```

Usage
-----

[](#usage)

### Adding Media via Model

[](#adding-media-via-model)

Add the `HasMedia` trait to your model:

```
use Jegex\Media\MediaCollections\Concerns\HasMedia;

class Post extends Model
{
    use HasMedia;

    // ...
}
```

Now you can attach media:

```
$post = Post::create(['title' => 'My Post']);

$post->addMedia('/path/to/image.jpg')
    ->toMediaCollection('images');

$post->getMedia('images')->first()->getUrl();
```

### Adding Media Directly (Standalone)

[](#adding-media-directly-standalone)

Since the `model_type` and `model_id` columns are nullable, you can create media without associating it to any model:

```
use Jegex\Media\MediaCollections\Models\Media;

// From a file path
$media = Media::createFromFile('/path/to/image.jpg');

// From string content
$media = Media::createFromString($imageContent, 'photo.jpg');

// From base64
$media = Media::createFromBase64($base64String, 'avatar.png');

// From a URL
$media = Media::createFromUrl('https://example.com/image.jpg');

// With custom options
$media = Media::createFromFile('/path/to/image.jpg', [
    'collection_name' => 'uploads',
    'name' => 'Custom Name',
    'disk' => 's3',
    'custom_properties' => ['source' => 'api'],
]);

// Access the media
echo $media->getUrl();
echo $media->toHtml();
```

This is useful for global media libraries, CDN assets, or when you don't need to associate media with a specific model.

### Retrieving Media

[](#retrieving-media)

```
// Get all media in a collection
$media = $model->getMedia('photos');

// Get first media
$media = $model->getFirstMedia('photos');

// Get media URL
$url = $media->getUrl();

// Get conversion URL
$url = $media->getUrl('thumb');

// Get temporary URL (S3)
$url = $media->getTemporaryUrl(now()->addMinutes(10));
```

### Media Properties

[](#media-properties)

```
$media->getName();        // 'my-image'
$media->getAltTxt();      // 'Alt text for accessibility'
$media->getCaption();     // 'Short caption'
$media->getDescription(); // 'Full description'
$media->file_name;        // 'my-image.jpg'
$media->mime_type;        // 'image/jpeg'
$media->size;             // 102400 (bytes)
```

### Custom Properties

[](#custom-properties)

```
$media->setCustomProperty('credits', 'John Doe');
$media->getCustomProperty('credits'); // 'John Doe'
$media->hasCustomProperty('credits'); // true
```

### Rendering Media

[](#rendering-media)

```
// Convert to HTML string
echo $media->toHtml();
//

// Blade component

// With conversion

```

Image Conversions
-----------------

[](#image-conversions)

Define conversions in your model:

```
use Jegex\Media\MediaCollections\Concerns\HasMedia;

class Post extends Model
{
    use HasMedia;

    public function registerMediaConversions(?Media $media = null): void
    {
        $this->addMediaConversion('thumb')
            ->width(200)
            ->height(200)
            ->optimize()
            ->queued();

        $this->addMediaConversion('preview')
            ->width(800)
            ->height(600)
            ->optimize();
    }
}
```

Retrieve converted images:

```
$thumbUrl = $media->getUrl('thumb');
$previewUrl = $media->getUrl('preview');
```

### Conversion Methods

[](#conversion-methods)

MethodDescription`width($px)`Set width`height($px)`Set height`fit(Fit $fit)`Set fit mode (contain, cover, fill, etc.)`format($format)`Set output format (webp, avif, jpg, png)`quality($quality)`Set output quality (1-100)`brightness($value)`Adjust brightness (-100 to 100)`contrast($value)`Adjust contrast`blur($value)`Apply blur effect`gamma($value)`Adjust gamma`flip($direction)`Flip image (h, v)`optimize()`Optimize output image`queued()`Process conversion on queue`nonQueued()`Process conversion synchronously`manipulate($name, $value)`Add custom manipulation### Image Generators

[](#image-generators)

The package supports multiple file types out of the box:

- **GenericImage** — JPEG, PNG, GIF, BMP
- **Webp** — WebP images
- **Avif** — AVIF images
- **Pdf** — PDF files (thumbnail extraction)
- **Svg** — SVG files
- **Video** — Video files (thumbnail extraction via FFMPEG)

### Image Optimizers

[](#image-optimizers)

Optimizers are applied automatically when `optimize()` is called:

FormatOptimizerJPEGJpegoptimPNGPngquant, OptipngSVGSvgoGIFGifsicleWebPCwebpAVIFAvifencResponsive Images
-----------------

[](#responsive-images)

Responsive images are generated automatically for image media files.

```
// Enable responsive images for a collection
$model->addMedia('/path/to/image.jpg')
    ->withResponsiveImages()
    ->toMediaCollection('photos');
```

The package calculates optimal widths using `FileSizeOptimizedWidthCalculator` (30% smaller per variation) and generates a blurred tiny placeholder for progressive loading.

Queue System
------------

[](#queue-system)

Conversions and responsive images can be processed asynchronously.

### Configuration

[](#configuration)

```
// config/media.php
'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'),
'queue_name' => env('MEDIA_QUEUE', ''),
'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true),
'queue_conversions_after_database_commit' => env('QUEUE_CONVERSIONS_AFTER_DB_COMMIT', true),
```

### Per-Conversion Queue Setting

[](#per-conversion-queue-setting)

```
$this->addMediaConversion('thumb')
    ->width(200)
    ->queued(); // Process on queue

$this->addMediaConversion('preview')
    ->width(800)
    ->nonQueued(); // Process immediately
```

### Jobs

[](#jobs)

JobDescription`PerformConversionsJob`Processes image conversions`GenerateResponsiveImagesJob`Generates responsive image variationsVapor Uploads
-------------

[](#vapor-uploads)

For Laravel Vapor deployments, enable the upload route:

```
// config/media.php
'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false),
'vapor_route_prefix' => 'media-vapor',
'vapor_route_middleware' => ['web', 'auth'],
```

### Routes

[](#routes)

MethodRouteDescriptionPOST`/media-vapor`Store new media from VaporPOST`/media-vapor/finished/{mediaId}`Mark media upload as finishedPOST`/media-vapor/parameters`Get upload parameters for S3 direct uploadZIP Export
----------

[](#zip-export)

Export media collections as ZIP archives for bulk downloads.

### Export from Model

[](#export-from-model)

```
// Stream ZIP directly to browser
return $post->getMediaCollectionZip('photos')->download('photos.zip');

// Save ZIP to a disk
$zipPath = $post->getMediaCollectionZip('photos')->saveToDisk('public', 'exports/photos.zip');
```

### Export from Single Media

[](#export-from-single-media)

```
use Jegex\Media\MediaCollections\Models\Media;

// Get ZIP for a single media item
$zip = $media->getZip('archive.zip');

// Or retrieve media by ID first
$media = Media::find(1);
$zip = $media->getZip('archive.zip');
```

### Filter by Conversion

[](#filter-by-conversion)

```
// Only include 'thumb' conversions in the ZIP
return $post->getMediaCollectionZip('photos', function ($zip, $media) {
    $zip->add($media, 'thumb');
})->download('thumbs.zip');
```

The ZIP export uses `maennchen/zipstream-php` for memory-efficient streaming. Files are added directly to the stream without loading them entirely into memory.

Facade Usage
------------

[](#facade-usage)

The package provides a `LaravelMedia` facade for convenient access to media operations without needing a model:

```
use Jegex\Media\Facades\LaravelMedia;

// Create media from file
$media = LaravelMedia::createFromFile('/path/to/image.jpg');

// Create media from string content
$media = LaravelMedia::createFromString($imageContent, 'photo.jpg');

// Create media from base64
$media = LaravelMedia::createFromBase64($base64String, 'avatar.png');

// Create media from URL
$media = LaravelMedia::createFromUrl('https://example.com/image.jpg');

// Retrieve media
$media = LaravelMedia::getMediaById(1);
$media = LaravelMedia::getMediaByIds([1, 2, 3]);
$collection = LaravelMedia::getMediaByCollection('avatars');

// Delete media
LaravelMedia::deleteMedia(1);

// Get package info
$maxSize = LaravelMedia::getMaxFileSize(); // 10485760 (10MB)
$defaultDisk = LaravelMedia::getDefaultDisk(); // 'public'
```

Configuration
-------------

[](#configuration-1)

The full configuration file (`config/media.php`):

```
return [
    // Default storage disk
    'disk_name' => env('MEDIA_DISK', 'public'),

    // Maximum file size (10MB default)
    'max_file_size' => 1024 * 1024 * 10,

    // Queue settings
    'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'),
    'queue_name' => env('MEDIA_QUEUE', ''),
    'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true),
    'queue_conversions_after_database_commit' => env('QUEUE_CONVERSIONS_AFTER_DB_COMMIT', true),

    // File naming and path generation
    'file_namer' => DefaultFileNamer::class,
    'path_generator' => DefaultPathGenerator::class,
    'file_remover_class' => DefaultFileRemover::class,
    'url_generator' => DefaultUrlGenerator::class,

    // URL versioning
    'version_urls' => false,

    // Image driver: gd, imagick, vips
    'image_driver' => env('IMAGE_DRIVER', 'gd'),

    // FFMPEG settings
    'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'),
    'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'),
    'ffmpeg_timeout' => env('FFMPEG_TIMEOUT', 900),
    'ffmpeg_threads' => env('FFMPEG_THREADS', 0),

    // Downloads
    'media_downloader' => DefaultDownloader::class,
    'media_downloader_ssl' => env('MEDIA_DOWNLOADER_SSL', true),

    // Temporary URL lifetime (minutes)
    'temporary_url_default_lifetime' => env('MEDIA_TEMPORARY_URL_DEFAULT_LIFETIME', 5),

    // S3 upload headers
    'remote' => [
        'extra_headers' => [
            'CacheControl' => 'max-age=604800',
        ],
    ],

    // Responsive images
    'responsive_images' => [
        'width_calculator' => FileSizeOptimizedWidthCalculator::class,
        'use_tiny_placeholders' => true,
        'tiny_placeholder_generator' => Blurred::class,
    ],

    // Loading attribute: 'lazy', 'eager', 'auto', or null
    'default_loading_attribute_value' => null,

    // Storage prefix
    'prefix' => env('MEDIA_PREFIX', ''),

    // Force lazy loading
    'force_lazy_loading' => env('FORCE_MEDIA_LIBRARY_LAZY_LOADING', true),

    // Vapor uploads
    'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false),
    'vapor_route_prefix' => 'media-vapor',
    'vapor_route_middleware' => ['web', 'auth'],
];
```

Environment Variables
---------------------

[](#environment-variables)

VariableDefaultDescription`MEDIA_DISK``public`Default storage disk`MEDIA_QUEUE``''`Queue name`QUEUE_CONNECTION``sync`Queue connection`QUEUE_CONVERSIONS_BY_DEFAULT``true`Queue conversions by default`QUEUE_CONVERSIONS_AFTER_DB_COMMIT``true`Run after database commit`IMAGE_DRIVER``gd`Image processing driver`FFMPEG_PATH``/usr/bin/ffmpeg`FFMPEG binary path`FFPROBE_PATH``/usr/bin/ffprobe`FFProbe binary path`FFMPEG_TIMEOUT``900`FFMPEG timeout (seconds)`FFMPEG_THREADS``0`FFMPEG thread count`MEDIA_DOWNLOADER_SSL``true`SSL verification for downloads`MEDIA_TEMPORARY_URL_DEFAULT_LIFETIME``5`Temporary URL lifetime (minutes)`MEDIA_PREFIX``''`Storage path prefix`FORCE_MEDIA_LIBRARY_LAZY_LOADING``true`Force lazy loading`ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS``false`Enable Vapor upload routesAdvanced Usage
--------------

[](#advanced-usage)

### Custom Path Generator

[](#custom-path-generator)

Create a custom path generator:

```
namespace App\Support;

use Jegex\Media\Support\PathGenerator\DefaultPathGenerator;
use Jegex\Media\MediaCollections\Models\Media;

class CustomPathGenerator extends DefaultPathGenerator
{
    public function getPath(Media $media): string
    {
        return $media->model_type.'/'.date('Y/m/d').'/'.$media->id;
    }
}
```

Register it in config:

```
'path_generator' => App\Support\CustomPathGenerator::class,
```

Or per-model:

```
'custom_path_generators' => [
    App\Models\Post::class => App\Support\PostPathGenerator::class,
],
```

### Media Lifecycle Events

[](#media-lifecycle-events)

The `MediaObserver` handles:

- **creating**: Sets highest order number
- **created**: Dispatches conversion and responsive image jobs
- **updating**: Handles file renaming (if `moves_media_on_update` is true)
- **deleting**: Removes all associated files from disk

### Blade Component

[](#blade-component)

```
{{-- Basic usage --}}

{{-- With custom class and loading --}}

{{-- With conversion --}}

{{-- Override loading attribute --}}

```

Testing
-------

[](#testing)

```
composer test
```

Run with coverage:

```
composer test-coverage
```

Run a specific test:

```
vendor/bin/pest --filter="test name"
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance94

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

5

Last Release

32d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e33533e1c902dffe83701a75f166b7c20b32150f9c04426e0c1bc49a22701ca7?d=identicon)[jegex](/maintainers/jegex)

---

Top Contributors

[![jegex](https://avatars.githubusercontent.com/u/219070132?v=4)](https://github.com/jegex "jegex (14 commits)")

---

Tags

laravellaravel-mediajegex

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/jegex-laravel-media/health.svg)

```
[![Health](https://phpackages.com/badges/jegex-laravel-media/health.svg)](https://phpackages.com/packages/jegex-laravel-media)
```

###  Alternatives

[finller/laravel-media

A flexible media library for Laravel

472.1k](/packages/finller-laravel-media)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.3M41](/packages/spatie-laravel-pdf)[spatie/laravel-medialibrary

Associate files with Eloquent models

6.1k41.3M594](/packages/spatie-laravel-medialibrary)[spatie/laravel-health

Monitor the health of a Laravel application

88011.3M149](/packages/spatie-laravel-health)[elegantly/laravel-media

A flexible media library for Laravel

475.6k4](/packages/elegantly-laravel-media)[wnx/laravel-backup-restore

A package to restore database backups made with spatie/laravel-backup.

210389.8k2](/packages/wnx-laravel-backup-restore)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
