Here is the step-by-step guide on How to Upload Avatar in Laravel.
Step 1: Download Laravel Project
composer create-project --prefer-dist laravel/laravel uploadavatarimages
Step 2: Setup SQL Database
//.env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=avatarimageupload DB_USERNAME=root DB_PASSWORD=
Laravel provides a quick way to scaffold all of the routes and views you need for authentication using one simple command:
php artisan make:auth
This command should be applied to new applications and install a layout view, registration and login views, and routes to all authentication endpoints.
Step 3: Modify the User Registration Form
We can add an avatar in resources >> views >> auth >> register.blade.php file.
<!-- register.blade.php --> <div class="form-group row"> <label for="avatar" class="col-md-4 col-form-label text-md-right">{{ __('Avatar (optional)') }}</label> <div class="col-md-6"> <input id="avatar" type="file" class="form-control" name="avatar"> </div> </div>
You can add enctype=”multipart/form-data” in the form. So the final code of register.blade.php looks like that:
<!-- register.blade.php --> @extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Register') }}</div> <div class="card-body"> <form method="POST" action="{{ route('register') }}" enctype="multipart/form-data"> @csrf <div class="form-group row"> <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label> <div class="col-md-6"> <input id="name" type="text" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus> @if ($errors->has('name')) <span class="invalid-feedback"> <strong>{{ $errors->first('name') }}</strong> </span> @endif </div> </div> <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required> @if ($errors->has('email')) <span class="invalid-feedback"> <strong>{{ $errors->first('email') }}</strong> </span> @endif </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required> @if ($errors->has('password')) <span class="invalid-feedback"> <strong>{{ $errors->first('password') }}</strong> </span> @endif </div> </div> <div class="form-group row"> <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required> </div> </div> <div class="form-group row"> <label for="avatar" class="col-md-4 col-form-label text-md-right">{{ __('Avatar (optional)') }}</label> <div class="col-md-6"> <input type="file" class="form-control" name="avatar" id="avatar"> </div> </div> <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Register') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
If you don’t have to add enctype, then you see an error like this:
Error : Spatie \ MediaLibrary \ Exceptions \ FileCannotBeAdded \ RequestDoesNotHaveFile The current request does not have a file in a key named `avatar`
Step 4: Install Spatie’s Media Library Package
Install Spatie’s Media Library Package via the Composer package manager.
composer require spatie/laravel-medialibrary:^7.0.0
After installing Spatie’s Media Library, you should publish the Spatie’s Media Library configuration using the vendor: publish Artisan command.
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="migrations"
We have migrations in our published folder:
php artisan migrate
It will generate a database table called media which uses Polymorphic Relations to store the data. You can see an error like this:
Error : [Illuminate\Database\QueryException] SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version of the right syntax to use ‘json not null, custom_properties
json not null, order_column
int unsigned nu’ at line 1 (SQL: create table m edia
(id
int unsigned not null auto_increment primary key, model_id
int unsigned, not null, model _type
varchar(191) not null, collection_name
varchar(191) not null, name
varchar(191) not null, file_name
varchar(191) not null, mime_type
varchar(191) null, disk
varchar(191) not null, size
int unsigned, not null, manipulations
json not null, custom_properties
json not null, order_column
int unsigned null, created_at
timestamp null, updated_at
timestamp null) default character set u
tf8mb4 collate utf8mb4_unicode_ci)
Possible Solution: you can change the datatype json to text in database >> migration >> create_media_table.php file.
The media table looks like that:
//create_media_table.php public function up() { Schema::create('media', function (Blueprint $table) { $table->increments('id'); $table->morphs('model'); $table->string('collection_name'); $table->string('name'); $table->string('file_name'); $table->string('mime_type')->nullable(); $table->string('disk'); $table->unsignedInteger('size'); $table->text('manipulations'); $table->text('custom_properties'); $table->text('responsive_images'); $table->unsignedInteger('order_column')->nullable(); $table->nullableTimestamps(); }); }
Step 5: Change the User Model File
Let’s develop that app >> User.php model to work with Media Library. But first, add the following code to the User.php file.
// User.php use Spatie\MediaLibrary\HasMedia\HasMediaTrait; use Spatie\MediaLibrary\HasMedia\HasMedia; class User extends Authenticatable implements HasMedia { // ... use HasMediaTrait; }
Here we can add HasMediaTrait and HasMedia. Both come from Media Library.
Back to our RegisterController and add some code to the create()method:
//RegisterController.php protected function create(array $data) { $user = User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); if (isset($data['avatar'])) { $user->addMediaFromRequest('avatar')->toMediaCollection('avatars'); } return $user; }
The images are uploaded to the storage directory to be public. Change the following.
Change disk config: Open filesystems.php, located in the config directory. Change the default parameter from local to public.
'default' => env('FILESYSTEM_DRIVER', 'public'),
Set a symlink from /public/storage to /storage/app/public folder with an Artisan command:
php artisan storage:link
Step 6: Show an Avatar Thumbnail
The use of that avatar is to show it in the top-right corner near the user’s name, so let’s do that.
But if the uploaded file is large? How could we resize it to, say, 60×60? This is where Media Library will support us again. All we want to do is set resize rules in the same app >> User model.
use Spatie\MediaLibrary\Models\Media; class User extends Authenticatable implements HasMedia { public function registerMediaConversions(Media $media = null) { $this->addMediaConversion('thumb') ->width(60) ->height(60); } }
This will be automatically stored in the conversations sub-folder for that item when uploading.
At last, this is how we show the avatar in resources >> views >> layout >> app.blade.php file.
<!--app.blade.php--> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> <img src="{{Auth::user()->getFirstMediaUrl('avatars', 'thumb') }}"> {{ Auth::user()->name }} <span class="caret"></span> </a>
The final code of the app.blade.php file looks like that.
<!--app.blade.php--> <!DOCTYPE html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light navbar-laravel"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> {{ config('app.name', 'Laravel') }} </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"> </ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> @guest <li><a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a></li> <li><a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a></li> @else <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> <img src="{{Auth::user()->getFirstMediaUrl('avatars', 'thumb') }}"> {{ Auth::user()->name }} <span class="caret"></span> </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> @endguest </ul> </div> </div> </nav> <main class="py-4"> @yield('content') </main> </div> </body> </html>
That’s it for this tutorial. Thanks for taking it.

Krunal Lathiya is a seasoned Computer Science expert with over eight years in the tech industry. He boasts deep knowledge in Data Science and Machine Learning. Versed in Python, JavaScript, PHP, R, and Golang. Skilled in frameworks like Angular and React and platforms such as Node.js. His expertise spans both front-end and back-end development. His proficiency in the Python language stands as a testament to his versatility and commitment to the craft.
after all, i a can’t register a user? no errors, can’t debug it. can you help me?
Hello, i have finally done this tutorial upto the last step but my image isnot appearing well.. What could be the problem please.
How to update that media collection?
Thanks for this tutorial Krunal but how can the update be done after the first avatar is uploaded. ? i am building a school project and i want the user to also have an option to delete the avatar. I am using laravel 5.7
Appreciating the time and effort you put into your site and in depth information you present.
It’s nice to come across a blog every once in a while that isn’t the same old rehashed material.
Excellent read! I’ve saved your site and I’m including your RSS feeds to my Google
account.
everything OK on local machine but in production ?????
storage:link doesn’t work in deploy to Heroku !
You have to use cloudinary or s3.
laravel 8 ERORR Argument 1 passed to Spatie\MediaLibrary\MediaRepository::getCollection() must be an instance of Spatie\MediaLibrary\HasMedia\HasMedia, instance of App\User given, called in F:\blog\vendor\spatie\laravel-medialibrary\src\HasMedia\HasMediaTrait.php on line 222 (View: F:\blog\resources\views\layouts\app.blade.php ?????????