Ionic 8 + Angular 20 Standalone Base64 Image Upload with Capacitor 5

by Didin J. on Aug 09, 2025 Ionic 8 + Angular 20 Standalone Base64 Image Upload with Capacitor 5

Learn to build an Ionic 8 + Angular 20 standalone app with Capacitor 5 to capture, convert, and upload base64 images from camera or gallery to a server.

In this tutorial, you will learn how to capture or pick an image in an Ionic 8 app, convert it to base64 (if needed), and upload it to a backend server using Angular 20’s standalone components and Capacitor 5. This replaces the older Cordova plugin method with the latest Capacitor APIs.

Prerequisites

Make sure you have:

  • Node.js v20+

  • Ionic CLI v7+

  • Basic understanding of Ionic and Angular

  • A running backend API that accepts file uploads (e.g., Express, Laravel, Django)


Step 1 – Create a New Ionic Angular Standalone App

npm install -g @ionic/cli
ionic start imageUploadApp blank --type=angular
cd imageUploadApp

Enable Capacitor integration:

ionic integrations enable capacitor

For the tutorial sanitation, run the app on the browser, then type this command.

ionic serve

That command will open the default browser with inspect mode and display this page.

Ionic 8, Angular 20 Standalone, and Capacitor Base64 Image Upload Example - ionic serve


Step 2 – Install Capacitor Camera & HTTP Dependencies

npm install @capacitor/camera
npm install @angular/common @angular/forms @angular/platform-browser @angular/platform-browser-dynamic @angular/router
ionic build
npx cap sync


Step 3 – Bootstrap Without NgModule

Open src/main.ts and replace with:

import { bootstrapApplication } from '@angular/platform-browser';
import { RouteReuseStrategy, provideRouter, withPreloading, PreloadAllModules } from '@angular/router';
import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';

import { routes } from './app/app.routes';
import { AppComponent } from './app/app.component';
import { provideHttpClient } from '@angular/common/http';

bootstrapApplication(AppComponent, {
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    provideIonicAngular(),
    provideRouter(routes, withPreloading(PreloadAllModules)),
    provideHttpClient(),
  ],
});


Step 4 – Create the App Component

Replace src/app/home/home.page.ts with:

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton } from '@ionic/angular/standalone';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
  imports: [CommonModule, IonButton, IonHeader, IonToolbar, IonTitle, IonContent],
})
export class HomePage {
  photoDataUrl: string | null = null;

  constructor(private http: HttpClient) { }

  async takePhoto() {
    const image = await Camera.getPhoto({
      quality: 80,
      resultType: CameraResultType.DataUrl,
      source: CameraSource.Camera
    });
    this.photoDataUrl = image.dataUrl!;
  }

  async pickImage() {
    const image = await Camera.getPhoto({
      quality: 80,
      resultType: CameraResultType.DataUrl,
      source: CameraSource.Photos
    });
    this.photoDataUrl = image.dataUrl!;
  }

  upload() {
    if (!this.photoDataUrl) return;

    const blob = this.dataUrlToBlob(this.photoDataUrl);
    const formData = new FormData();
    formData.append('file', blob, 'photo.jpg');

    this.http.post('https://your-server.com/upload', formData)
      .subscribe({
        next: res => console.log('Upload success', res),
        error: err => console.error('Upload failed', err)
      });
  }

  private dataUrlToBlob(dataUrl: string): Blob {
    const arr = dataUrl.split(',');
    const mime = arr[0].match(/:(.*?);/)![1];
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], { type: mime });
  }
}

Replace src/app/home/home.page.html with:

<ion-header>
  <ion-toolbar>
    <ion-title>Image Upload</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <ion-button expand="block" (click)="takePhoto()">Take Photo</ion-button>
  <ion-button expand="block" (click)="pickImage()"
    >Pick from Gallery</ion-button
  >

  @if (photoDataUrl) {
  <div class="preview">
    <img [src]="photoDataUrl" alt="Preview" />
    <ion-button color="success" expand="block" (click)="upload()"
      >Upload</ion-button
    >
  </div>
  }
</ion-content>


Step 5 – Update Android/iOS Permissions

Android

Add Android platform:

npm install @capacitor/android
npx cap add android

Open android/app/src/main/AndroidManifest.xml and ensure:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

iOS

Add iOS platform:

npm install @capacitor/ios 
npx cap add ios

n ios/App/App/Info.plist:

<key>NSCameraUsageDescription</key>
<string>We need access to your camera to take photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to choose images.</string>


Step 6 – Build and Run

ionic build
npx cap open android
npx cap open ios

Run on a real device to test the camera and upload.


Step 7 – Backend Upload Script Example (Node.js Express)

import express from 'express';
import multer from 'multer';
import cors from 'cors';

const app = express();
app.use(cors());

const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('file'), (req, res) => {
  console.log(req.file);
  res.json({ message: 'File uploaded successfully' });
});

app.listen(3000, () => console.log('Server running on port 3000'));


Conclusion

You’ve now modernized the old Ionic 3 + Angular 5 + Cordova base64 upload flow to Ionic 8 + Angular 20 standalone + Capacitor 5.
This approach:

  • Works on Android, iOS, and Web

  • Uses Capacitor APIs instead of deprecated Cordova plugins

  • Keeps the code clean and standalone-friendly

Don't worry, you can compare the tutorial to the working source code on our GitHub.

We know that building beautifully designed Ionic apps from scratch can be frustrating and very time-consuming. Check Ionic 6 - Full Starter App and save development and design time. Android, iOS, and PWA, 100+ Screens and Components, the most complete and advanced Ionic Template.

That's just the basics. If you need more deep learning about Ionic, Angular, and TypeScript, you can take the following cheap course:

Thanks!