HTML Web SQL

Beginner
⏱️ 14 min read
📚 Updated: Jul 2026
🎯 5 Examples + 3 Try It
openDatabase / executeSql

Introduction

Web SQL Database is a deprecated web standard for storing structured data in the browser using a SQL-based relational database. Under the hood, browsers that supported it used SQLite, so familiar SQL statements—CREATE TABLE, INSERT, SELECT, UPDATE, and DELETE—work much like server-side databases.

Although Web SQL is no longer recommended for new projects, understanding how it works is valuable for maintaining legacy applications and learning how client-side storage evolved. This page is the overview and entry point for the Web SQL series; follow-up tutorials cover each CRUD operation in depth.

What You’ll Learn

01

openDatabase

Open or create a DB.

02

Transactions

Wrap every query.

03

executeSql

Run SQL safely.

04

ResultSet

Read SELECT rows.

05

IndexedDB

Modern alternative.

What Is Web SQL?

Web SQL is a browser API that lets web applications store data in a relational database on the client. You interact with it using SQL—the same language used by MySQL, PostgreSQL, and SQLite—which makes it approachable if you already know databases.

The two core methods are:

  • openDatabase(name, version, displayName, size) — opens an existing database or creates a new one.
  • db.transaction(callback) — runs one or more tx.executeSql() calls as an atomic unit.

Data persists across page reloads (until the user clears site data), making Web SQL useful for offline-first prototypes, cached records, and small local datasets—when the browser still exposes the API.

Why Web SQL Was Deprecated

The W3C deprecated Web SQL in 2010 and stopped pursuing it as an official standard. The main reasons were:

  • Single implementation — Web SQL depended on one vendor’s SQLite build, not a spec every browser could implement independently.
  • Inconsistent support — Firefox never shipped it; Chrome later removed it; only some Safari versions kept it.
  • Better alternativeIndexedDB provides a standardized, cross-browser key-value and object store without tying the web platform to SQL.

For new code in 2026, treat Web SQL as read-only knowledge—learn it to debug old apps, but build new storage on IndexedDB or server APIs.

Basic Concepts

Before writing queries, know these relational-database terms:

  • Database — a named container for tables (opened with openDatabase).
  • Table — rows and columns of related data (created with CREATE TABLE).
  • SQL — Structured Query Language for defining schema and reading/writing rows.
  • Transaction — a group of statements that succeed or fail together, preserving consistency.
  • ResultSet — the object returned by SELECT queries; iterate with results.rows.item(i).

Creating a Database

Call openDatabase to open or create a database. The fourth argument is an estimated size in bytes (here, 2 MB). Then run CREATE TABLE inside a transaction:

js
const db = openDatabase('myDatabase', '1.0', 'My App DB', 2 * 1024 * 1024);

db.transaction(function (tx) {
  tx.executeSql(
    'CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY, name TEXT, email TEXT)'
  );
});

CREATE TABLE IF NOT EXISTS is idempotent—safe to run on every page load without errors if the table already exists.

Executing SQL Queries

Use tx.executeSql(sql, params, successCallback, errorCallback) inside a transaction. Always bind dynamic values with ? placeholders instead of string concatenation:

js
db.transaction(function (tx) {
  tx.executeSql(
    'INSERT INTO contacts (id, name, email) VALUES (?, ?, ?)',
    [1, 'Jane Doe', 'jane@example.com'],
    function (tx, result) {
      console.log('Inserted row, insertId:', result.insertId);
    },
    function (tx, error) {
      console.error('Insert failed:', error.message);
      return true; /* roll back transaction */
    }
  );
});

The same pattern applies to UPDATE, DELETE, and SELECT. See the dedicated tutorials: INSERT, SELECT / Retrieve, UPDATE, and DELETE.

Transactions and Error Handling

Every Web SQL operation must run inside db.transaction(). You can pass three callbacks to transaction itself—work, error, and success:

js
db.transaction(
  function (tx) {
    tx.executeSql('UPDATE contacts SET name = ? WHERE id = ?', ['Jane Smith', 1]);
  },
  function (error) {
    console.error('Transaction failed:', error.message);
  },
  function () {
    console.log('Transaction committed successfully');
  }
);

Inside executeSql, return true from the error callback to roll back the transaction. Without it, the transaction may continue and leave data in an inconsistent state.

Working with Results

When you run a SELECT query, the success callback receives a ResultSet. Use results.rows.length and results.rows.item(index) to read each row:

js
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT * FROM contacts ORDER BY name',
    [],
    function (tx, results) {
      const len = results.rows.length;
      for (let i = 0; i < len; i++) {
        const row = results.rows.item(i);
        console.log(row.id, row.name, row.email);
      }
    }
  );
});

For write operations, check result.rowsAffected (updates/deletes) or result.insertId (inserts) to confirm what changed.

⚡ Quick Reference

TaskAPI / SQL pattern
Open databaseopenDatabase(name, ver, label, bytes)
Feature detectif (window.openDatabase) { ... }
Run SQLdb.transaction(tx => tx.executeSql(...))
Bind parametersexecuteSql('... WHERE id = ?', [id])
Read SELECT rowsresults.rows.item(i)
Modern replacementIndexedDB
Open
openDatabase(...)

First step

Wrap
db.transaction()

Required

Query
tx.executeSql()

Run SQL

Safe
?, ?, ?

Bindings

Examples Gallery

These examples demonstrate the Web SQL lifecycle from opening a database to reading rows. Use Try It Yourself to run live demos. If your browser does not expose openDatabase, the demo shows a clear status message.

📚 Getting Started

Open a database and set up your first table.

Example 1 — Feature Detect & Open Database

Always check for API support before calling openDatabase.

js
if (!window.openDatabase) {
  console.warn('Web SQL is not supported in this browser.');
} else {
  const db = openDatabase('myApp', '1.0', 'My Application', 1024 * 1024);
  console.log('Database opened:', db);
}
Try It Yourself

How It Works

The guard prevents runtime errors in browsers that removed Web SQL. When supported, openDatabase returns a Database object immediately—schema setup happens in the next transaction.

Example 2 — Create a Table

Define a contacts table with an integer primary key and text columns.

js
const db = openDatabase('myApp', '1.0', 'My Application', 2 * 1024 * 1024);

db.transaction(function (tx) {
  tx.executeSql(
    'CREATE TABLE IF NOT EXISTS contacts (' +
    'id INTEGER PRIMARY KEY, name TEXT, email TEXT' +
    ')'
  );
}, function (err) {
  console.error('Setup failed:', err.message);
}, function () {
  console.log('Table ready');
});
Try It Yourself

How It Works

Schema changes are SQL statements like any other—they still run inside transaction. The success callback fires when the table exists and the transaction commits.

📈 CRUD Basics

Insert rows and read them back with standard SQL.

Example 3 — Insert a Row

Add a contact with bound parameters—never concatenate user input into SQL strings.

js
db.transaction(function (tx) {
  tx.executeSql(
    'INSERT INTO contacts (id, name, email) VALUES (?, ?, ?)',
    [1, 'John Doe', 'john@example.com'],
    function (tx, result) {
      console.log('insertId:', result.insertId);
    }
  );
});
Try It Yourself

How It Works

The ? placeholders map to the array [1, 'John Doe', 'john@example.com'] in order. SQLite escapes values safely, preventing SQL injection.

Example 4 — Select All Rows

Retrieve every contact and loop through the ResultSet.

js
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT * FROM contacts',
    [],
    function (tx, results) {
      for (let i = 0; i < results.rows.length; i++) {
        console.log(results.rows.item(i));
      }
    }
  );
});
Try It Yourself

How It Works

Each row is a plain object with column names as keys. For larger result sets, add ORDER BY or LIMIT in SQL rather than sorting in JavaScript.

Example 5 — Complete HTML Page

A self-contained mini app: create the database, add records with a button, and list them on the page.

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Web SQL Example</title>
</head>
<body>
  <h1>Web SQL Address Book</h1>
  <button type="button" id="addBtn">Add Contact</button>
  <button type="button" id="listBtn">List Contacts</button>
  <p id="status"></p>
  <ul id="list"></ul>

  <script>
    if (!window.openDatabase) {
      document.getElementById('status').textContent =
        'Web SQL is not supported in this browser.';
    } else {
      const db = openDatabase('addressBook', '1.0', 'Address Book', 2 * 1024 * 1024);

      db.transaction(function (tx) {
        tx.executeSql(
          'CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY, name TEXT)'
        );
      });

      document.getElementById('addBtn').addEventListener('click', function () {
        const id = Date.now();
        db.transaction(function (tx) {
          tx.executeSql(
            'INSERT INTO contacts (id, name) VALUES (?, ?)',
            [id, 'New Contact'],
            function () {
              document.getElementById('status').textContent = 'Added id ' + id;
            }
          );
        });
      });

      document.getElementById('listBtn').addEventListener('click', function () {
        const list = document.getElementById('list');
        list.innerHTML = '';
        db.transaction(function (tx) {
          tx.executeSql('SELECT * FROM contacts', [], function (tx, results) {
            for (let i = 0; i < results.rows.length; i++) {
              const row = results.rows.item(i);
              const li = document.createElement('li');
              li.textContent = row.name + ' (id ' + row.id + ')';
              list.appendChild(li);
            }
          });
        });
      });
    }
  </script>
</body>
</html>
Try It Yourself

How It Works

On load, the script creates the table. Each button click starts a new transaction—inserts and selects are separate atomic operations. This is the pattern most legacy Web SQL apps follow.

🚀 Common Use Cases

  • Offline drafts — save form data locally when the network is unavailable.
  • Cached catalogs — store product or article rows for faster repeat visits.
  • Legacy mobile web apps — maintain older Safari-based apps that still call openDatabase.
  • Learning SQL — experiment with relational queries without a server.
  • Migration planning — understand existing Web SQL schemas before moving to IndexedDB.

🧠 How Web SQL Works

1

openDatabase

Browser opens or creates a SQLite file for your origin.

Connect
2

transaction

Your callback receives a SQLTransaction object.

Wrap
3

executeSql

SQLite runs your statement with optional bound parameters.

Query
=

Persisted data

Rows survive reloads until the user clears site storage.

💡 Best Practices

✅ Do

  • Feature-detect with window.openDatabase
  • Bind all dynamic values with ? placeholders
  • Handle error callbacks and return true to roll back
  • Keep stored data small—Web SQL is for lightweight client cache
  • Use IndexedDB for new projects
  • Read the CRUD tutorials for deeper coverage

❌ Don’t

  • Build new features on Web SQL in 2026
  • Concatenate user input into SQL strings
  • Call executeSql outside a transaction
  • Store large blobs or entire media files
  • Assume Web SQL works in Chrome or Firefox
  • Skip error handling on write operations

📝 Notes

  • Web SQL is deprecated—prefer IndexedDB for new apps.
  • All executeSql calls must run inside db.transaction().
  • Version upgrades use changeVersion() (advanced; rarely needed in tutorials).
  • Estimated size is a hint; browsers may enforce quotas per origin.
  • Clearing browser data wipes the entire Web SQL database for that site.
  • Continue to DELETE, INSERT, SELECT, and UPDATE for operation-specific guides.

Browser Support

Web SQL was never implemented in Firefox. Chrome shipped it early but removed it in Chrome 97. Safari on iOS/macOS historically supported it on some versions. Always feature-detect with window.openDatabase before running demos or legacy code.

Baseline · Since HTML

Web SQL API

Web SQL was never implemented in Firefox. Chrome shipped it early but removed it in Chrome 97. Safari on iOS/macOS historically supported it on some versions. Always feature-detect with window.openDatabase before running demos or legacy code.

25% Modern browser support
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
Web SQL API Deprecated

Bottom line: For cross-browser storage today, use IndexedDB.

Conclusion

Web SQL brought familiar SQL to the browser: openDatabase to connect, transaction to wrap work, and executeSql to run statements with safe parameter bindings. You can create tables, insert rows, and read results entirely on the client.

Because the API is deprecated, treat this knowledge as essential for legacy maintenance, not greenfield development. When you are ready to go deeper, continue with the DELETE, INSERT, SELECT, and UPDATE tutorials—or start fresh with IndexedDB for modern apps.

Key Takeaways

Knowledge Unlocked

Five things to remember about Web SQL

Use these points when reading or maintaining client-side SQL code.

5
Core concepts
🔄 02

transaction

Wrap SQL.

Required
💻 03

executeSql

Run queries.

CRUD
🔒 04

? bindings

Stay safe.

Security
⚠️ 05

Deprecated

Use IndexedDB.

Modern

❓ Frequently Asked Questions

Web SQL Database is a deprecated browser API that stores structured data in a local SQLite database. You open it with openDatabase() and run standard SQL statements (CREATE TABLE, INSERT, SELECT, UPDATE, DELETE) inside db.transaction() using tx.executeSql().
Web SQL is deprecated and removed from most modern browsers, including current Chrome. Firefox never implemented it. Treat this tutorial as educational for legacy code; use IndexedDB for new projects.
The W3C stopped standardizing Web SQL because it relied on a single vendor's SQLite implementation, making cross-browser behavior hard to guarantee. IndexedDB became the recommended client-side database API.
Call openDatabase(name, version, displayName, estimatedSize). Example: openDatabase('myApp', '1.0', 'My App DB', 2 * 1024 * 1024). The method returns a Database object or throws if Web SQL is unavailable.
The Web SQL API requires every executeSql call to run within db.transaction(). Transactions group statements atomically—if one fails, the whole transaction can roll back, keeping data consistent.
Use IndexedDB for new browser storage. It is supported across modern browsers, handles larger datasets, and works with structured JavaScript objects. localStorage is fine for simple key-value strings only.
Did you know?

Web SQL uses the same SQL dialect as SQLite, including features like INSERT OR REPLACE and CREATE TABLE IF NOT EXISTS. If you know SQLite from mobile or embedded development, most of that knowledge transfers directly to Web SQL.

Practice Web SQL basics

Open a database, create a table, and insert rows in the Try It editor—with friendly messages when the API is unavailable.

Open Try It editor →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful