Laravel 8 Image File Upload With Inertia Js Vue 3 Example

In this tutorial, we’ll explore how to upload an image file with validation using Laravel Inertia.js in Vue 3. For this section, we’ll use Laravel 8 with Laravel Breeze, Laravel Inertia.js Vue 3, and Laravel File Storage for image upload. Let’s dive in.

Step 1: Create Laravel Project & Connect Database

Installing a fresh new laravel application.

composer create-project --prefer-dist laravel/laravel inertia-file-upload

Now, you have to connect the laravel app to the database, hence open the .env configuration file and add the database credentials as suggested below.

.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=database_user_name
DB_PASSWORD=database_password

Step 2: Install Breeze & Setup Inertia Js Vue 3

Install laravel breeze via composer.

composer require laravel/breeze --dev

Next, run below command.

php artisan breeze:install

install breeze with vue 3.

php artisan breeze:install vue

And final install Dependencies

npm install && npm run dev 

To create the symbolic link, you may use the storage:link Artisan command:

php artisan storage:link

Step 3: Create Model, Migration and Controller

Create a Model, Migration and Controller.

php artisan make:model Image -mc 

Add field in migration.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateImagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->text('image');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('images');
    }
}

app/Models/Image.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Image extends Model
{
    protected $fillable = ['image'];
    
    use HasFactory;
}

Setp 4: Create Image Request for Validation

To Create Image request run below command.

php artisan make:request StoreImage 

app/Http/Requests/StoreImage.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreImage extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'image' => 'required|image|mimes:jpeg,jpg,png,gif,svg|max:2048'
        ];
    }
}

app/Http/Controllers/ImageController.php

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreImage;
use App\Models\Image;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Request;
use Inertia\Inertia;
use Illuminate\Support\Facades\Storage;


class ImageController extends Controller
{
    public function index()
    {
        $images = Image::latest()->get();

        return Inertia::render('Image/Index', ['images' => $images]);
    }

    public function create()
    {
        return Inertia::render('Image/Create');
    }

    public function store(StoreImage $request)
    {

    $image_path = '';

    if ($request->hasFile('image')) {
        $image_path = $request->file('image')->store('image', 'public');
    }

    $data = Image::create([
        'image' => $image_path,
    ]);


        return Redirect::route('image.index');
    }

}

Step 5: Define Image routes

web.php

<?php

use App\Http\Controllers\ImageController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return Inertia::render('Welcome', [
        'canLogin' => Route::has('login'),
        'canRegister' => Route::has('register'),
        'laravelVersion' => Application::VERSION,
        'phpVersion' => PHP_VERSION,
    ]);
});

Route::get('/dashboard', function () {
    return Inertia::render('Dashboard');

})->middleware(['auth', 'verified'])->name('dashboard');
Route::get('image', [ImageController::class,'index'])->name('image.index');
Route::get('image/create', [ImageController::class,'create'])->name('image.create');
Route::post('image', [ImageController::class,'store'])->name('image.store');
require __DIR__.'/auth.php';

Step 6: Create a view file

app/resources/js/Pages/Image/Create.vue

<template>
    <head title="Dashboard" />

    <BreezeAuthenticatedLayout>
        <template #header>
            <h2 class="text-xl font-semibold leading-tight text-gray-800">
                Image Create
            </h2>
        </template>

        <div class="py-12">
            <div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
                <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
                    <div class="p-6 bg-white border-b border-gray-200">
                        <form @submit.prevent="submit">
                            <div>
                                <label for="File">File Upload</label>
                                <input
                                    type="file"
                                    @change="previewImage"
                                    ref="photo"
                                    class="w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring-1 focus:ring-blue-600"
                                />
                                <img
                                    v-if="url"
                                    :src="url"
                                    class="w-full mt-4 h-80"
                                />
                                <div
                                    v-if="errors.image"
                                    class="font-bold text-red-600"
                                >
                                    {{ errors.image }}
                                </div>
                            </div>

                            <div class="flex items-center mt-4">
                                <button
                                    class="px-6 py-2 text-white bg-gray-900 rounded"
                                >
                                    Save
                                </button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </BreezeAuthenticatedLayout>
</template>

<script>
    import BreezeAuthenticatedLayout from "@/Layouts/Authenticated.vue";
    import BreezeLabel from "@/Components/Label";
    import { Head } from "@inertiajs/inertia-vue3";
    import { useForm } from "@inertiajs/inertia-vue3";
    export default {
        components: {
            BreezeAuthenticatedLayout,
            Head,
        },
        props: {
            errors: Object,
        },
        data() {
            return {
                url: null,
            };
        },
        setup() {
            const form = useForm({
                image: null,
            });

            return { form };
        },
        methods: {
            submit() {
                if (this.$refs.photo) {
                    this.form.image = this.$refs.photo.files[0];
                }
                this.form.post(route("image.store"));
            },
            previewImage(e) {
                const file = e.target.files[0];
                this.url = URL.createObjectURL(file);
            },
        },
    };
</script>
 Inertia Js  Vue 3 image upload

Validation Image

image validation

app/resources/js/Pages/Image/Index.vue

<template>

    <Head title="Dashboard" />

    <BreezeAuthenticatedLayout>
        <template #header>
            <h2 class="text-xl font-semibold leading-tight text-gray-800">
                Image Index
            </h2>
        </template>

        <div class="py-12">
            <div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
                <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
                    <div class="p-6 bg-white border-b border-gray-200">
                        <div class="mb-4">
                            <Link class="px-6 py-2 mb-2 text-green-100 bg-green-500 rounded"
                                :href="route('image.create')">
                            Create
                            </Link>
                        </div>
                        <table class="w-full">
                            <thead class="font-bold bg-gray-300 border-b-2">
                                <td class="px-4 py-2">ID</td>
                                <td class="px-4 py-2">image</td>
                                <td class="px-4 py-2">Action</td>
                            </thead>
                            <tbody>
                                <tr v-for="image in images" :key="image.id">
                                    <td class="px-4 py-2">{{ image.id }}</td>
                                    <td class="px-4 py-2">
                                        <img :src="showImage() + image.image" class="object-cover h-40 w-80" />
                                    </td>
                                    <td class="px-4 py-2">Action</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </BreezeAuthenticatedLayout>
</template>

<script>
import BreezeAuthenticatedLayout from "@/Layouts/Authenticated.vue";
import BreezeNavLink from "@/Components/NavLink.vue";
import { Head } from "@inertiajs/inertia-vue3";
import { Link } from "@inertiajs/inertia-vue3";
export default {
    components: {
        BreezeAuthenticatedLayout,
        Head,
        BreezeNavLink,
        Link,
    },
    methods: {
        showImage() {
            return "/storage/";
        },
    },

    props: {
        images: Object,
    },
};
</script>
 Image File Upload With Inertia Js

Note: Always try to open two terminal windows – one for the server and the other for executing the commands below.

npm run dev
saim ansari
saim ansari

I'm Saim Ansari, a full-stack developer with 4+ years of hands-on experience who thrives on building web applications that leave a lasting impression. When it comes to tech, I'm particularly adept at Laravel, React, Tailwind CSS, and the Tall Stack