Summary

Learn how to implement an image preview feature before upload using HTML, JavaScript, and Tailwind CSS. Includes drag-and-drop functionality, multiple file selection, and responsive design.

Article Body

How to Create Image Preview Before Upload - HTML, JavaScript & Tailwind CSS Tutorial
How to Create Image Preview Before Upload - HTML, JavaScript & Tailwind CSS Tutorial

In this tutorial, we'll build a modern image upload interface that shows previews of selected images before uploading. The solution includes drag-and-drop functionality, multiple file selection, and a clean UI built with Tailwind CSS.

Step 1: Set Up the Basic HTML Structure

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Preview Before Upload</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center p-4">
    <div class="max-w-2xl w-full bg-white rounded-xl shadow-lg overflow-hidden p-6 space-y-6">
        <!-- Content will go here -->
    </div>
</body>
</html>

Step 2: Create the Upload Area

Add this inside the main container div:

<div class="text-center">
    <h1 class="text-3xl font-bold text-gray-800">Image Preview Before Upload</h1>
    <p class="text-gray-600 mt-2">Preview your images before uploading them</p>
</div>

<div class="space-y-4">
    <!-- Upload Area -->
    <div class="flex flex-col items-center justify-center border-2 border-dashed border-gray-300 rounded-lg p-8 transition hover:border-blue-500 bg-gray-50">
        <input type="file" id="image-upload" accept="image/*" multiple class="hidden">
        <label for="image-upload" class="cursor-pointer flex flex-col items-center">
            <!-- SVG icon and instructions -->
        </label>
    </div>
</div>

Step 3: Add the Preview Section

<!-- Preview Section -->
<div>
    <h2 class="text-lg font-medium text-gray-800 mb-2">Preview</h2>
    <div id="preview-container" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4">
        <div class="flex items-center justify-center h-32 bg-gray-100 rounded-lg text-gray-400">
            No images selected
        </div>
    </div>
</div>

Step 4: Include Upload Information and Button

<!-- Upload Info -->
<div id="upload-info" class="hidden bg-blue-50 border border-blue-200 rounded-lg p-4">
    <div class="flex items-center">
        <!-- Info icon -->
        <span class="text-sm text-blue-700">
            <span id="selected-count">0</span> image(s) ready for upload
        </span>
    </div>
</div>

<!-- Upload Button -->
<button 
    id="upload-btn" 
    class="w-full px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition disabled:opacity-50 disabled:cursor-not-allowed"
    disabled
>
    Upload Images
</button>

Step 5: Add JavaScript Functionality

document.addEventListener('DOMContentLoaded', function() {
    const imageUpload = document.getElementById('image-upload');
    const previewContainer = document.getElementById('preview-container');
    const uploadInfo = document.getElementById('upload-info');
    const selectedCount = document.getElementById('selected-count');
    const uploadBtn = document.getElementById('upload-btn');
    let selectedFiles = [];

    // Drag and drop event handlers
    const dropZone = document.querySelector('label[for="image-upload"]').parentElement;
    
    dropZone.addEventListener('dragover', (e) => {
        e.preventDefault();
        dropZone.classList.add('border-blue-500', 'bg-blue-50');
    });

    // ... (include all the JavaScript from your original code)
    
    function resetUploader() {
        selectedFiles = [];
        imageUpload.value = '';
        updatePreview();
        updateUploadInfo();
    }
});

Step 6: Implement the Core Functions

Add these functions to handle the main logic:

function handleFiles(files) {
    // Filter only image files
    const imageFiles = Array.from(files).filter(file => file.type.match('image.*'));
    
    if (imageFiles.length === 0) {
        alert('Please select image files only');
        return;
    }

    selectedFiles = imageFiles;
    updatePreview();
    updateUploadInfo();
}

function updatePreview() {
    previewContainer.innerHTML = '';
    
    if (selectedFiles.length === 0) {
        previewContainer.innerHTML = `
            <div class="flex items-center justify-center h-32 bg-gray-100 rounded-lg text-gray-400">
                No images selected
            </div>
        `;
        return;
    }

    selectedFiles.forEach((file, index) => {
        const reader = new FileReader();
        reader.onload = function(e) {
            const previewItem = document.createElement('div');
            previewItem.className = 'relative group h-32 bg-gray-100 rounded-lg overflow-hidden';
            previewItem.innerHTML = `
                <img src="${e.target.result}" alt="Preview" class="w-full h-full object-cover">
                <div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-30 transition flex items-center justify-center opacity-0 group-hover:opacity-100">
                    <button class="remove-image-btn p-2 bg-red-500 text-white rounded-full hover:bg-red-600 transition" data-index="${index}">
                        <!-- Trash icon -->
                    </button>
                </div>
                <div class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-50 text-white text-xs p-1 truncate">
                    ${file.name}
                </div>
            `;
            previewContainer.appendChild(previewItem);
        };
        reader.readAsDataURL(file);
    });

    // Add event listeners to remove buttons
    setTimeout(() => {
        document.querySelectorAll('.remove-image-btn').forEach(btn => {
            btn.addEventListener('click', (e) => {
                e.stopPropagation();
                const index = parseInt(btn.getAttribute('data-index'));
                selectedFiles.splice(index, 1);
                updatePreview();
                updateUploadInfo();
            });
        });
    }, 100);
}

Step 7: Add Upload Information Updates

function updateUploadInfo() {
    if (selectedFiles.length > 0) {
        uploadInfo.classList.remove('hidden');
        selectedCount.textContent = selectedFiles.length;
        uploadBtn.disabled = false;
        
        // Calculate total size
        const totalSize = selectedFiles.reduce((total, file) => total + file.size, 0);
        const sizeInMB = (totalSize / (1024 * 1024)).toFixed(2);
        
        // Update info text
        uploadInfo.querySelector('span').innerHTML = `
            ${selectedFiles.length} image(s) selected (${sizeInMB} MB) ready for upload
        `;
    } else {
        uploadInfo.classList.add('hidden');
        uploadBtn.disabled = true;
    }
}

Step 8: Implement the Upload Button

uploadBtn.addEventListener('click', () => {
    if (selectedFiles.length) {
        // In a real application, you would upload the files here
        alert(`Preparing to upload ${selectedFiles.length} image(s)`);
        console.log('Files to upload:', selectedFiles);
        
        // Simulate upload
        setTimeout(() => {
            alert('Upload completed! (This is a demo)');
            resetUploader();
        }, 1500);
    }
});

Conclusion

You've now created a fully functional image preview before upload component with:

  • Drag and drop support

  • Multiple file selection

  • Image preview thumbnails

  • File information display

  • Responsive design with Tailwind CSS

This can be easily integrated into any web application. For production use, you would replace the simulated upload with actual server communication using Fetch API or Axios.