Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to preview images before upload in Angular

Showing a preview before uploading gives users immediate confidence that they selected the right file, dramatically reducing mistakes and re-uploads. As the creator of CoreUI with Angular development experience since 2014, I include image preview in every avatar upload or gallery feature in CoreUI Angular templates. The browser’s FileReader API reads the selected file as a Data URL and Angular’s change detection updates the preview image source reactively. This requires no libraries — just the native File API and a reactive component property.

Use FileReader.readAsDataURL to generate a preview URL from the selected file.

// image-upload.component.ts
import { Component } from '@angular/core'
import { CommonModule } from '@angular/common'

@Component({
  selector: 'app-image-upload',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="upload-container">
      <input
        type="file"
        accept="image/*"
        (change)="onFileSelected($event)"
        #fileInput
        style="display: none"
      />

      <div class="preview-area" (click)="fileInput.click()">
        <img
          *ngIf="previewUrl; else placeholder"
          [src]="previewUrl"
          alt="Preview"
          class="preview-image"
        />
        <ng-template #placeholder>
          <div class="placeholder">Click to select an image</div>
        </ng-template>
      </div>

      <p *ngIf="error" class="text-danger">{{ error }}</p>

      <button
        *ngIf="previewUrl"
        (click)="upload()"
        [disabled]="uploading"
      >
        {{ uploading ? 'Uploading...' : 'Upload Image' }}
      </button>
    </div>
  `
})
export class ImageUploadComponent {
  previewUrl: string | null = null
  error = ''
  uploading = false
  private selectedFile: File | null = null

  onFileSelected(event: Event): void {
    const input = event.target as HTMLInputElement
    const file = input.files?.[0]

    if (!file) return

    this.error = ''

    if (!file.type.startsWith('image/')) {
      this.error = 'Please select an image file'
      return
    }

    if (file.size > 5 * 1024 * 1024) {
      this.error = 'Image must be smaller than 5MB'
      return
    }

    this.selectedFile = file
    this.readFileAsDataUrl(file)
  }

  private readFileAsDataUrl(file: File): void {
    const reader = new FileReader()
    reader.onload = (e) => {
      this.previewUrl = e.target?.result as string
    }
    reader.readAsDataURL(file)
  }

  async upload(): Promise<void> {
    if (!this.selectedFile) return

    this.uploading = true
    const formData = new FormData()
    formData.append('image', this.selectedFile)

    try {
      const res = await fetch('/api/images', { method: 'POST', body: formData })
      if (!res.ok) throw new Error('Upload failed')
      this.uploading = false
    } catch (err: any) {
      this.error = err.message
      this.uploading = false
    }
  }
}

FileReader.readAsDataURL converts the file to a Base64 Data URL. Assigning it to previewUrl and binding it to [src] displays the image immediately without any server round trip. The reader.onload callback runs asynchronously — Angular’s zone integration picks up the change automatically.

Preview with Drag and Drop

Accept dropped images and show preview instantly.

@Component({
  selector: 'app-drop-image',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div
      class="drop-zone"
      [class.active]="isDragging"
      (dragover)="onDragOver($event)"
      (dragleave)="isDragging = false"
      (drop)="onDrop($event)"
    >
      <img *ngIf="previewUrl" [src]="previewUrl" alt="Preview" />
      <p *ngIf="!previewUrl">Drop an image here</p>
    </div>
  `
})
export class DropImageComponent {
  isDragging = false
  previewUrl: string | null = null

  onDragOver(event: DragEvent): void {
    event.preventDefault()
    this.isDragging = true
  }

  onDrop(event: DragEvent): void {
    event.preventDefault()
    this.isDragging = false
    const file = event.dataTransfer?.files[0]
    if (file?.type.startsWith('image/')) {
      const reader = new FileReader()
      reader.onload = (e) => { this.previewUrl = e.target?.result as string }
      reader.readAsDataURL(file)
    }
  }
}

The drag and drop pattern is identical — just the file source changes from input.files to event.dataTransfer.files. Always call event.preventDefault() in dragover to enable dropping.

Best Practice Note

This is the same preview approach used in CoreUI Angular templates for user avatar uploads and image galleries. For multiple image previews, call readAsDataURL for each file and push results into an array. After previewing, send the actual File object (not the Data URL) to the server — Data URLs are Base64-encoded and roughly 33% larger than the original file. See how to upload files in Angular for the complete upload implementation.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author