Also available in: Français

Some time ago, when I was doing research on customizable CSS radio buttons and checkboxes, I had in mind to do the same thing with the “file” form input. So I did a test in pure CSS. Then I left the idea aside.

Finally I went back on it to complete the basic idea with visual textual feedback. Indeed if you try this version of the custom input-file in CSS, you will see that nothing indicates that an image is selected. This is rather annoying for a user..

I then tried to do it in JS (vanilla, no jQuery), and it’s not that complex, finally.

Custom input file demo

The HTML base

To make it as “natural” as possible, we will do it with a label element and an input:file.
The label will be used as a triggerable element, like a button, to activate the selection of a file.

<div class="input-file-container">
<input class="input-file" id="my-file" type="file">
<label for="my-file" class="input-file-trigger" tabindex="0">
Select a file...
<p class="file-return"></p>

On purpose, I fill here several classes to manipulate the elements with CSS and JS without depending on HTML elements.
tabindex="0" allows us to make the label focusable especially for keyboard navigation.

Some styles

The container will be positioned in relative position in order to be able to place the input in absolute position within it.
We will therefore end up with the label of the field used as a “trigger” (sensitive to clicks and keyboard keys), with the file input on top of it, but transparent, also used as a trigger.
We are going to work on the internal margin (padding) of this field in order to superpose it over the label, thus making the label and the invisible field sensitive on an area that is almost identical.

/* styles if JS activated */
.js .input-file-container {
position: relative;
width: 225px;

.js .input-file-trigger {
display: block;
padding: 14px 45px;
background: #39D2B4;
color: #fff;
font-size: 1em;
transition: all .4s;
cursor: pointer;

.js .input-file {
position: absolute;
top: 0;
left: 0;
width: 225px;
padding: 14px 0;
opacity: 0;
cursor: pointer;

/* some styles for interactions */
.js .input-file:hover + .input-file-trigger,
.js .input-file:focus + .input-file-trigger,
.js .input-file-trigger:hover,
.js .input-file-trigger:focus {
background: #34495E;
color: #39D2B4;

/* our visual feedback P */
.file-return {
margin: 0;

.file-return:not(:empty) {
margin: 1em 0;

.js .file-return {
font-style: italic;
font-size: .9em;
font-weight: bold;

/* only if the paragraph is not empty, we complete the visual feedback with this text */
.js .file-return:not(:empty):before {
content: "Selected file: ";
font-style: normal;
font-weight: normal;

For the rest of the graphic choices (colors, typography, etc.) I let you do by yourself, it’s secondary for what I propose.

Visual feedback with JS

This code is proposed in Vanilla JS (i.e. without JS Framework), it is possible to adapt it to your favorite framework if you use one in your project.

// Adds .js class to HTML

// init our variables
var fileInput = document.querySelector( ".input-file" ),
button = document.querySelector( ".input-file-trigger" ),
the_return = document.querySelector(".file-return");

// Trigger when Space bar or Enter is hit
button.addEventListener( "keydown", function( event ) {
if ( event.keyCode == 13 || event.keyCode == 32 ) {

// Trigger when the label is clicked
button.addEventListener( "click", function( event ) {
return false;

// Display a visual feedback
fileInput.addEventListener( "change", function( event ) {
the_return.innerHTML = this.value;

The actions are relatively simple here, I hope the comments will be enough for you 🙂
If not, don’t hesitate to ask in the comments.

The result and the presentation here are very basic, it will then be up to you to add small visual effects: an attached file icon on the button for example, or a customization of the text return according to the language, etc…

Demonstraion Fork on

Some articles on forms to go further

If you didn’t know that, you can also style some form elements like radio buttons, checkboxes, or even optimize experience one one-time-code input?

Good reading!