Image Uploads Using Laravel 5.4 With VueJS

/ PHP / by Paul Robinson / 10 Comments
This post was published back on April 25, 2017 and may be outdated. Please use caution when following older tutorials or using older code. After reading be sure to check for newer procedures or updates to code.

It’s been a little while since I made a ‘proper’ tutorial so I have decided to put this one together in the hopes that it helps those starting out with Laravel and Vue. I assume you have basic knowledge of HTML & CSS, plus I assume you have at least had a quick perusal of the Laravel Documentation and Vue documentation. If you haven’t you may find some of the concepts here a little foreign.

Starting Notes

I’m unable to provide a demo for this project however it will cover how to create a basic image upload system using DropzoneJS, Vue and Laravel. There are also a couple of prerequisites.

Laravel and Vue are best used with NPM so you will need to have Node installed. As of writing version 6.10.2 LTS is the version I am currently using and Laravel’s npm install worked fine. Knowledge of NPM would be helpful, but I will be going over all commands so it is not essential.

You will also need Composer installed in your development environment. If you are using Docker, make sure it is installed in your container. If you are using a VM make sure it is installed on your VM. If you are using a PHP install you will need to install it on your PC.

A final note is that I am using Docker (Laradock). You should be able to use Docker, Vagrant or even just a standard webserver like XAMPP, but I have not used the latter and so cannot confirm it does actually work.

Setting Up Laravel

It would take too long to cover all of the various types of install such as Docker, VM, etc so I will just be doing a quick recap of the install instructions from the Laravel docs. I prefer the Composer install method so we will follow that one.

Open up a terminal (I use cmder for Windows) and navigate to the folder you wish to install Laravel in and run:

This will install into the directory you are currently in. If you wish to create a directory replace . with the name of the directory you wish to create.

Remember that your web server needs to be set to serve the public folder inside the laravel install. Instructions for doing that depend on your setup but for Apache this VHosts tutorial on Digital Ocean might help, and for Nginx this Server Blocks tutorial on Digital Ocean might help.

After the install has completed we need to install all of the Node packages. This will allow us to use webpack.

Once complete we can now use webpack. To keep things simple and concentrate on the upload system we are just going to use the files given to us by Laravel for it’s welcome page.

Creating a Controller

First let’s create our controller. We’ll use Artisan to create the controller.

I encourage you to name your future controllers better than I have here, but for the purposes of this tutorial that will be fine. While we are using Artisan let’s make sure to make our public storage folder accessible via a URL. To do that run:

This will make your storage/public folder accessible via

Open up your controller (/app/http/controllers/MainController.php) and let’s get started. We are going to write out all of our methods here and each one will translate to a route. They will be as follows.

  1. Home – The entry point and home page. Returns HTML.
  2. Upload – The upload route, responds to POST requests only. Returns JSON Response.
  3. List – Image listing route. Simple directory listing. Returns JSON Response.

So, let’s write these out.

Be careful not to miss that I have included the Storage Namespace at the top of the file since we are using the Storage Facade to help store our image in the file system.

Some fairly simple code here, but let’s go through everything.

Home Method

Simply returns the welcome blade template. Currently it just shows the Laravel logo, but we’ll edit that soon.

Upload Method

Takes the injected Request object and checks for a file and if that file is valid. Also stores the file using Laravel’s file system Facade. Returns a error via JSON if not valid or no file exists. Stores and returns a JSON object if the file is stored successfully.

List Method

Since I am not using a DB in this tutorial, to help keep things simple, I am just listing the files found in the directory. This would probably be best replaced with something that lists the files from a data store like a Database rather than a file system like this. To be on the safe side pre-make your images folder inside /storage/app/public.

Now that we have our methods, let’s setup the routes. We do that in the routes file at /routes/web.php.

Now that the routes have been created we can start to work on our Vue code.

Vue Components – Image List Component

Laravel automatically installs Vue when you do your initial npm install, so let’s take advantage of that. Before we start we need to pull in Dropzone for our uploader to use.

We are going to be using the excellent Vue Dropzone by rowanwins as it bundles Dropzone as a ready made vue component.

Now let’s get webpack running.

It will take a little while the first time, but should be quicker after that initial run. This command will watch for file changes and then recompile when it detects them. There is also:

If you prefer not to use file watching.

Now you should be able to see that Laravel gives us an example component in the /resources/assets/js/components folder. Let’s use that. Rename the file to ImageList.vue and place the following code in there.

Let’s explain this a little.

First up is our template. It uses Bootstrap classes since it is pre-included with Laravel. The only new part here is the v-for attribute and the :src attribute. v-for works exactly like a for... loop in JavaScript. Here it repeats the tag but it would also repeat any children if the tag were a div for example. The :src attribute allows us to use a variable as the value for the tag’s src attribute. Somewhat like src="{{ file }}".

Now for the Vue code. First up we import Axios. Axios is a simple promise based HTTP client that is great for AJAX requests if you don’t need jQuery. jQuery is included with Laravel, but to make things interesting I thought we’d use Axios here instead.

On mount of the component we run a method called refresh(). We create a url prop. The url prop will be used to set the url to retrieve the list of files. This enables us to change the route at a later date without needing to edit the component. We create a files data variable to store our list of files. Finally we create the refresh method. It makes a simple get request to the URL set on the url prop using Axios and then sets the files variable to the value of the response. Note that the response data is held in

In out /resources/assets/js/app.js file we will need to inform Vue of our component. To do that replace the example component with this one:

Vue Components – Upload Component

Now we will create the upload component.

Create a file called Upload.vue in your components folder and then add the component to our app.js file.

Now let’s write out our upload component. This one will be using the Vue Dropbox component.

Here we have some simple HTML again but this time we have a custom tag called dropzone this is a the Vue Dropzone component. We give it an ID, pass in the upload path, pass a CSRF token and define a method to run on a successful upload. These extra attributes are props defined by the Vue Dropzone component and allow us to customise things easily. The last one is not a prop and is actually an event triggered by Vue Dropzone that we can respond to.

In the JavaScript we include the Vue Dropzone component. We create a csrf prop so we can pass in our csrf token, an action prop so we can set the upload url from the parent component, and we create the showSuccess method to fire when the Dropzone triggers the vdropzone-success event. For the moment it just logs something to the console but we’ll do something better with it soon.

Creating The Home View

Okay, we are nearly done. Now we’ll create the home view. Open up /resources/views/welcome.blade.php replace the code in there with this:

Here we have added an ID to our main container. This matches the ID used when Vue is initialised in our app.js file. We’ve also added in our upload and image-list components giving them the csrf, action and url prop values.

Here is the CSS if you want to style it the same way. Place this in /resources/sass/app.scss. Replace any code that is already there.

It is just simple styling using the Laravel welcome screen styles.

With that you should be ready to go with a basic example. If you visit your Laravel application you should see a Dropzone and you should be able to drop an image or click and upload. You may have noticed though that you’ll only see your list of images if you refresh… That’s rubbish. Let’s fix that.

Vue Events – The Bus

In Vue you can only listen to and notify of parent/child component events, however you can get around this by using an event bus. Instead of the parent/child emitting and responding to events the bus emits and responds inside the component’s code.

Why is this relevant? Well we need to re-request data on our image-list component when the Dropzone component has finished a successful upload. Since the image-list component is not related to the Upload or Dropzone component we have to use an event bus. Let’s make one.

Create a new file called bus.js next to app.js. Enter the following:

Writing our bus as a file like this allows us to import it as a module which allows us to use the same event bus in multiple components.

Now in our ImageList.vue file we need to modify our code. Here is the whole code with the newly added lines highlighted.

We have imported our bus and added some code here that will be ran when the created hook is ran by Vue. This will be triggered when the bus is sent the uploaded event. If the file it receives was uploaded successfully we trigger a refresh of the file list. Simple.

Now in our Upload.vue component.

This one is even simpler. We import our bus again and use the $emit method given to us by our event bus to trigger the uploaded event when Dropzone fires it’s vdropzone-success event. We pass the file data along with it too.

Mission Complete!

That’s it, we are finally done. You should now have a working uploader using Laravel, Vue and DropzoneJS.


Here is a list of questions that you may have, if you have a question I haven’t answered here please let me know via the comments.

Why do we need a second CSRF token?

Laravel includes the CSRF token into Axios’ headers. You just need to add:

To your main blade file, as we did in this tutorial. In fact if you didn’t add this your requests to the /list route would be refused due to a token mismatch. However Vue Dropzone makes separate requests to upload the files and so we need to pass the CSRF token to it separately add send it with it’s headers.

Why didn’t you do it this way?

As with everything web development based, I’m sure there is. This is just my way of doing it. Let me know in the comments if you have improvements, or tips, I’m always learning too.

Why not use a State Management Pattern?

I wanted to keep things simple and using an Event Bus was about as simple as I could make it. It works and it isn’t too nasty.


If you are hungry for more, how about trying to add in some things I missed out.

Image Validation

Use Laravel’s server side validation to make sure the file is an image, and use DropzoneJS’s validation as a quick client side image validation.

Store File Names In Database

As I mentioned originally I just listed the images from the directory for speed and to keep things simple, but it would be best to use a database instead. Try to implement something using Eloquent.

Verify URL Prop & Respond To List Route Errors

In a real world example you would want to check the url prop in the ImageList.vue component has a value, and also have something happen if the response from the route was an error.


Author’s gravatar

Hello Robin, thanks a lot for your time and this nice article.

I have followed althought I am getting a 401 unauthorized response.

I am using laravel 5.4 and spark.

Any ideas?

Author’s gravatar author

Hi Luis,

I’m afraid I haven’t used Spark so I can’t speak to that side of it, but a 401 would suggest there is some sort of authentication system in place?

Now I’m not 100% sure as I haven’t done this in a while, but if your upload route is protected by the authentication system you will need to give DropzoneJS a way to access it as I don’t believe it will carry your access privileges through with its AJAX request.

There are a few ways to do it. Roll your own token generated from your authenticated user data and check it on the other side, or use something like Laravel Passport which I believe has a API token system specifically for these situations.

This is just a guess from the error you mentioned, but hopefully it help put you on the right track.

Author’s gravatar

Hello Paul,

Thanks a lot for a fast a warm response, I really appreciate it.

After reading some posts I realized I was using the auth:api since I am trying to implement the application using a vue with this middleware from the beginning.

After I changed my route to the web.php route system it is uploading the file nicely since it uses another middleware.

Although I still have not been able to make it work using the auth:api middleware, I tried the following with no results:

Admin Edit: Your code was stripped by WordPress. Please post the code on a service like pastebin or a Git Gist if you have a Github account.

In the header request I can see the header variables requiered:X-CSRF-TOKEN and X-XSRF-TOKEN, although it just do not work.

I will post it here in case i find the solution.

Have you ever tried :

It looks like a very nice upload component for vue 2 as well

Thanks a have a great day.

Author’s gravatar author

Hi Luis,

No problem.

If you posted some code in your response it was probably stripped by WordPress. Could you try reposting it on a code service like pastebin and posting the link?

I believe your code is passing the CSRF check as I think that gives a different error than the 401 you a receiving. Instead I think it is failing on a type of authentication middleware. You’ll need to see which one that is and generate the required key or token to allow DropzoneJS’ requests to pass.

I say believe and think as I haven’t really had the chance to use Laravel’s API routes since they were introduced so I am going on knowledge I have gained from reading other articles and the limited experience I do have with them.

I have bookmarked those Vue Elements as I haven’t come across them before, but they do look amazing. I may see if I can do a tutorial on using them very soon if that would be helpful.

Author’s gravatar

Hello again Paul,

Yes you are right, I guess wordpress stripped out the code, here it is in pastedbin:

And it would be great to have that tutorial as well with

Great writing!



Author’s gravatar author

No worries. Thanks for coming back and posting it on Pastebin.

I’ll definitely try to get to that soon. I really like the look of those components.

I think the issue you are hitting is definitely related to authentication via the API routes. I would advise looking at Laravel’s section on how to consume your own API via JavaScript using Laravel Passport. From a quick read it does sound like it would solve the issue you are seeing.

Author’s gravatar

Apart from vue, I think you can improve the experience of file uploading in laravel by using jquery or AJAX. However, laravel file upload form without any front-end rendering or script is not that bad. You can easily create the form with proper validation and everything.

Author’s gravatar author

Hello Oliver,

Thank you for your comment. Of course you can use jQuery, a AJAX library like Axios or even just plain JavaScript, but this tutorial was specifically for those wanting to use Laravel along with Vue to upload their images.

I do however recommend you make a standard file upload form, with no JavaScript, before you attempt to go onto something like Vue. It is always a good idea to know the basics first.

Older Comments
Newer Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

I'll keep your WordPress site up-to-date and working to its best.

Find out more