Skip to content
Apache Cordova Guide — Build Mobile Apps with HTML, CSS, and JS

Apache Cordova Guide — Build Mobile Apps with HTML, CSS, and JS

DodaTech Updated Jun 7, 2026 9 min read

Apache Cordova wraps your web application in a native WebView, exposing device APIs through a unified JavaScript interface so you can build cross-platform mobile apps using the web technologies you already know.

What You’ll Learn

You’ll use the Cordova CLI to scaffold projects and add platforms, configure apps with config.xml, access native features through plugins (camera, geolocation, file), understand hybrid vs native tradeoffs, and build a complete app deployable to iOS and Android.

Why Cordova Matters

Cordova was the original hybrid mobile framework and is still used by thousands of production apps. It lets web developers target mobile platforms without learning Java, Kotlin, Swift, or Objective-C. Doda Browser’s companion survey app uses Cordova because it needs camera access on both platforms but doesn’t require native performance — development speed and code reuse matter more.

Cordova Learning Path

    flowchart LR
  A[HTML, CSS, JavaScript] --> B[Cordova CLI]
  B --> C[config.xml]
  C --> D[Platform Add]
  D --> E[Plugins]
  E --> F[Native APIs]
  F --> G[Build & Deploy]
  B:::current

  classDef current fill:#E8E8E8,color:#000,stroke:#333,stroke-width:2px
  
Prerequisites: Solid JavaScript and HTML fundamentals. Basic knowledge of mobile platform tooling (Android Studio or Xcode) is helpful for building and testing.

How Cordova Works

Cordova creates a native wrapper — a thin native app shell containing a full-screen WebView. Your HTML, CSS, and JavaScript run inside this WebView like a web page, but with access to native device capabilities through plugins:

    graph TD
  A[Your Web App] --> B[Cordova JavaScript API]
  B --> C[Plugin Layer]
  C --> D[Native APIs]
  D --> E[Camera]
  D --> F[GPS]
  D --> G[File System]
  D --> H[Contacts]
  D --> I[Accelerometer]
  

The JavaScript API communicates with native code through a bridge. When you call navigator.camera.getPicture(), Cordova marshals the request to native Java (Android) or Objective-C (iOS), captures the photo, and returns the result to JavaScript.

Getting Started with Cordova CLI

# Install Cordova globally
npm install -g cordova

# Create a new project
cordova create dodatech-app com.dodatech.app "DodaTech App"
cd dodatech-app

# Add platforms
cordova platform add android
cordova platform add ios  # macOS only

# Check platform requirements
cordova requirements

The create command generates this structure:

dodatech-app/
├── www/
│   ├── index.html          # Entry point
│   ├── css/
│   ├── js/
│   └── img/
├── config.xml              # App configuration
├── platforms/              # Platform-specific code
├── plugins/                # Installed plugins
├── hooks/                  # Build lifecycle hooks
└── package.json

Configuring with config.xml

The config.xml file is the heart of your Cordova app — it controls metadata, preferences, and permissions:

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.dodatech.app"
        version="1.0.0"
        xmlns="http://www.w3.org/ns/widgets"
        xmlns:cdv="http://cordova.apache.org/ns/1.0">
  
  <name>DodaTech App</name>
  <description>A hybrid mobile app built with Apache Cordova</description>
  <author email="dev@dodatech.com" href="https://dodatech.com">
    DodaTech
  </author>
  
  <content src="index.html" />
  
  <!-- Splash screen preferences -->
  <preference name="SplashScreen" value="screen" />
  <preference name="SplashScreenDelay" value="3000" />
  <preference name="AutoHideSplashScreen" value="true" />
  
  <!-- Status bar -->
  <preference name="StatusBarOverlaysWebView" value="false" />
  <preference name="StatusBarBackgroundColor" value="#2c3e50" />
  
  <!-- Android-specific -->
  <platform name="android">
    <preference name="AndroidMinSdkVersion" value="24" />
    <preference name="AndroidTargetSdkVersion" value="33" />
    <preference name="orientation" value="portrait" />
  </platform>
  
  <!-- iOS-specific -->
  <platform name="ios">
    <preference name="TargetDevice" value="iPhone,iPad" />
    <preference name="orientation" value="all" />
  </platform>
  
  <!-- Permissions -->
  <edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
    <application android:usesCleartextTraffic="true" />
  </edit-config>
</widget>

Using Plugins

Plugins are the bridge between JavaScript and native APIs. Install them with the CLI:

cordova plugin add cordova-plugin-camera
cordova plugin add cordova-plugin-geolocation
cordova plugin add cordova-plugin-file
cordova plugin add cordova-plugin-contacts

Camera Plugin Example

// Capture a photo
function capturePhoto() {
  navigator.camera.getPicture(
    function(imageData) {
      // Success callback — display the photo
      const img = document.getElementById('photo');
      img.src = 'data:image/jpeg;base64,' + imageData;
      img.style.display = 'block';
    },
    function(error) {
      // Error callback
      console.error('Camera error:', error);
      alert('Failed to capture photo: ' + error);
    },
    {
      quality: 80,
      destinationType: Camera.DestinationType.DATA_URL,
      sourceType: Camera.PictureSourceType.CAMERA,
      encodingType: Camera.EncodingType.JPEG,
      saveToPhotoAlbum: true
    }
  );
}

// Select from gallery
function selectFromGallery() {
  navigator.camera.getPicture(onSuccess, onFail, {
    quality: 80,
    destinationType: Camera.DestinationType.FILE_URI,
    sourceType: Camera.PictureSourceType.PHOTOLIBRARY
  });
}

Output: On Android or iOS, tapping “Capture Photo” opens the native camera app. After capturing, the photo appears in the <img> element as a base64-encoded data URL. Error handling shows an alert if the camera isn’t available.

Geolocation Plugin Example

function getCurrentPosition() {
  navigator.geolocation.getCurrentPosition(
    function(position) {
      const lat = position.coords.latitude;
      const lng = position.coords.longitude;
      const accuracy = position.coords.accuracy;
      
      document.getElementById('location').innerHTML = `
        <p>Latitude: ${lat.toFixed(6)}</p>
        <p>Longitude: ${lng.toFixed(6)}</p>
        <p>Accuracy: ${accuracy.toFixed(1)} meters</p>
      `;
      
      // Show on map
      showOnMap(lat, lng);
    },
    function(error) {
      console.error('Geolocation error:', error);
      switch(error.code) {
        case error.PERMISSION_DENIED:
          alert('Location permission denied');
          break;
        case error.POSITION_UNAVAILABLE:
          alert('Location unavailable');
          break;
        case error.TIMEOUT:
          alert('Location request timed out');
          break;
      }
    },
    {
      enableHighAccuracy: true,
      timeout: 10000,
      maximumAge: 60000
    }
  );
}

Output: The native GPS or network location provider determines the device’s position. Coordinates and accuracy are displayed. Error handling catches permission denials, timeouts, and unavailable signals — common in indoor environments.

File System Plugin Example

function saveNoteToFile(content, filename) {
  // Request persistent storage
  window.requestFileSystem(
    LocalFileSystem.PERSISTENT,
    0,
    function(fileSystem) {
      // Create or open the file
      fileSystem.root.getFile(
        filename,
        { create: true, exclusive: false },
        function(fileEntry) {
          // Write content
          fileEntry.createWriter(function(writer) {
            writer.onwriteend = function() {
              console.log('File saved to:', fileEntry.toURL());
              alert('Note saved successfully!');
            };
            writer.onerror = function(e) {
              console.error('Write failed:', e.toString());
            };
            writer.write(content);
          });
        },
        function(error) {
          console.error('File error:', error.code);
        }
      );
    },
    function(error) {
      console.error('File system error:', error.code);
    }
  );
}

function listSavedFiles() {
  window.requestFileSystem(
    LocalFileSystem.PERSISTENT,
    0,
    function(fileSystem) {
      const dirReader = fileSystem.root.createReader();
      dirReader.readEntries(
        function(entries) {
          const list = document.getElementById('file-list');
          list.innerHTML = '';
          entries.forEach(function(entry) {
            if (entry.isFile) {
              const li = document.createElement('li');
              li.textContent = entry.name + ' (' + entry.size + ' bytes)';
              list.appendChild(li);
            }
          });
        },
        function(error) {
          console.error('Read error:', error.code);
        }
      );
    }
  );
}

Output: The app saves text content to persistent device storage. On subsequent launches, it lists all saved files with their sizes. The file persists across app restarts and is accessible only to the app (sandboxed storage).

Hybrid vs Native Tradeoffs

AspectHybrid (Cordova)Native
Development speedFast — one codebaseSlower — per-platform
PerformanceWebView-boundFull native speed
UI feelWeb-like, slight lagPlatform-native feel
API accessPlugin-dependentFull SDK access
App sizeLarger (WebView engine)Smaller per-platform
MaintenanceSingle codebaseMultiple codebases

Cordova is ideal for: internal enterprise apps, prototypes, content-focused apps, and teams with web expertise but limited mobile experience.

Security Angle: Secure Plugin Usage

// Validate file paths to prevent directory traversal
function safeReadFile(userProvidedPath) {
  // Sanitize the path — remove any ".." components
  const sanitized = userProvidedPath
    .replace(/\.\.\//g, '')  // Remove parent directory references
    .replace(/\.\.\\/g, '')  // Windows variant
    .replace(/~/g, '');      // Remove home directory references
  
  // Ensure path starts with expected directory
  if (!sanitized.startsWith('/notes/')) {
    console.error('Invalid path — must be in /notes/ directory');
    return;
  }
  
  window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
    fs.root.getFile(sanitized, { create: false }, 
      function(entry) {
        // Read and display file
      },
      function(error) {
        console.error('File access denied');
      }
    );
  });
}

DodaTech’s mobile security scanner uses these same validation patterns to prevent path traversal attacks when scanning user-specified file locations.

Common Mistakes Beginners Make

  1. Forgetting deviceready event: Cordova plugins are not available until the deviceready event fires. Accessing plugins before this event causes undefined errors. Always wrap your app initialization in this event listener.

  2. Testing in browser without plugins: Running cordova serve or opening index.html directly in a desktop browser won’t load native plugins. Always test on real devices or emulators.

  3. Not handling permission denials: Android 6+ and iOS require runtime permission requests. Plugins fail gracefully, but your app must handle the error callbacks and prompt users to grant permissions.

  4. Blocking the UI thread: Heavy JavaScript operations freeze the WebView. Use Web Workers or chunk operations with requestAnimationFrame for smooth scrolling.

  5. Not specifying Content Security Policy (CSP): Cordova apps are vulnerable to XSS without CSP headers. Add <meta http-equiv="Content-Security-Policy"> in your index.html.

  6. Platform-specific CSS issues: Android WebView and iOS WKWebView render CSS differently. Test both platforms and use CSS resets or framework normalization.

Practice Questions

  1. What event must fire before using Cordova plugins?
  2. How do you add the camera plugin to a Cordova project?
  3. What is the difference between DATA_URL and FILE_URI for camera images?
  4. Why might you choose Cordova over React Native?
  5. What is the purpose of config.xml?

Answers:

  1. The deviceready event. Cordova fires this event when the native layer is initialized and plugins are available.
  2. Run cordova plugin add cordova-plugin-camera in the project directory. The plugin is downloaded from npm and configured for all added platforms.
  3. DATA_URL returns the image as a base64-encoded string (memory-intensive for large images). FILE_URI returns a file path to the saved image (more memory-efficient).
  4. Cordova uses standard web technologies (HTML, CSS, JavaScript) with no framework dependency, ideal for teams with web expertise who want to reuse existing code.
  5. It configures app metadata, permissions, splash screen, orientation, and platform-specific preferences. It’s equivalent to AndroidManifest.xml + Info.plist combined.

Challenge

Build a field inspection app: use the camera plugin to capture photos of equipment, the geolocation plugin to tag inspection location, the file plugin to save inspection reports as JSON files, and a list view to browse past inspections.

Real-World Task

Create a customer feedback app: collect user feedback with a form (name, rating, comments), attach photos using the camera plugin, save submissions to local storage with the file plugin, and export all feedback as a CSV file for analysis.

FAQ

Is Cordova still relevant in 2026?
: Yes. While Capacitor is the newer alternative from the Ionic team, Cordova remains widely used with a mature plugin ecosystem. Thousands of production apps still use Cordova, and it continues to receive updates.
Can I use Cordova with frameworks like React or Vue?
: Yes. Cordova is framework-agnostic. Build your UI with any framework — the built files go into the www/ directory. The Ionic Framework started as a Cordova-based UI toolkit.
How do I debug Cordova apps?
: Use Safari Web Inspector (iOS) or Chrome DevTools (Android) connected to the running app. console.log output appears in the inspector. GapDebug is another option for cross-platform debugging.
What is the app size overhead of Cordova?
: The WebView engine and native shell add approximately 20-30MB. This is larger than a truly native app but acceptable for most enterprise and content apps.
Do I need a Mac for iOS builds?
: Yes. Building and signing iOS apps requires Xcode on macOS. For Android, Windows, Linux, and macOS all work.

Try It Yourself

cordova create dodatech-camera-app com.dodatech.camera "Camera Demo"
cd dodatech-camera-app
cordova platform add android
cordova plugin add cordova-plugin-camera

Edit www/js/index.js to add a “Take Photo” button that captures a photo using the camera plugin and displays it. Build and run on a device.

What’s Next

Related topics: JavaScript, HTML, CSS, Ionic

What’s Next

Congratulations on completing this Apache Cordova tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro