Web SQL lets you add rows with a normal SQL INSERT statement, run through tx.executeSql() inside a transaction. Bind values with ?, then read result.insertId for the new row’s id when you use an auto-increment primary key.
Pair this with SELECT to read rows back, and UPDATE to change them later. For new apps, prefer IndexedDB insert.
Deprecated API.
Web SQL is obsolete and removed from modern Chrome. Use this page to maintain legacy code or learn SQL inserts. New projects should use IndexedDB.
Related ideas.
INSERT OR REPLACE upserts by primary key. INSERT OR IGNORE skips duplicates. Always bind user values with ?—never concatenate SQL strings.
Syntax
Syntax
syntax.sql
INSERT INTOtable_name (columns) VALUES (?, …)
In Web SQL, wrap that statement in executeSql with bound arguments:
JS
tx.executeSql(
'INSERT INTO items (name, qty) VALUES (?, ?)',
[name, qty],
function (tx, result) { /* success */ },
function (tx, error) { /* failure */ }
);
Reference
Parameters
The executeSql call for an insert takes these arguments:
Argument
Type
Description
sqlStatement
String
The INSERT INTO ... VALUES (...) text, with ? placeholders for dynamic values.
arguments
Array
Values bound to each ? in order—e.g. ['Notebook', 2].
successCallback
Function (optional)
Called as (tx, result). Inspect result.insertId and result.rowsAffected.
errorCallback
Function (optional)
Called as (tx, error). Return true to roll back the transaction.
List column names explicitly. Bind values with ?—never paste user input into the SQL string.
Result
Return Value
executeSql does not return a useful value itself. On success, your callback receives (tx, result). For INSERT, use result.insertId for the new row id and result.rowsAffected to confirm a row was added.
Property
Type
Meaning after INSERT
result.insertId
Number
The new row’s id (SQLite rowid / INTEGER PRIMARY KEY)
result.rowsAffected
Number
How many rows were inserted (usually 1)
result.rows
SQLResultSetRowList
Empty for INSERT (no selected rows)
rowsAffected values:
Value
What it means
0
No row was inserted (common with INSERT OR IGNORE when a duplicate key exists).
1 or more
That many rows were added (or replaced with INSERT OR REPLACE).
Flow
How It Works
A typical single-row insert follows these steps:
Open the database with openDatabase(name, version, displayName, size).
Start a transaction: db.transaction(function (tx) { ... }).
Ensure the table exists with CREATE TABLE IF NOT EXISTS if needed.
Run INSERT via tx.executeSql(sql, args, success, error).
Read insertId / rowsAffected in the success callback.
Insert a contact, list rows, or clear the table. Status updates below. Many modern browsers no longer support Web SQL—if so, the status box will say so.
Uses a private demo database when Web SQL is available. Prefer IndexedDB for new work.
Status
Checking Web SQL support…
Code
Examples
Short, copy-ready snippets. Use Try It Yourself for a live editor. Demos need a browser that still exposes openDatabase.
Feature-detectopenDatabase before running any SQL.
Create the table on load with IF NOT EXISTS.
On button click, run INSERT ... VALUES (?, ?).
Show insertId in the status text.
Edge Cases & Pitfalls
Most INSERT bugs come from unbound input, missing tables, or ignoring constraint conflicts.
inject
Bind with ?
Never build SQL with string concatenation from user input. Pass values in the arguments array.
unique
Duplicate keys fail
Plain INSERT errors on unique conflicts. Use OR REPLACE or OR IGNORE when that is intentional.
schema
Create the table first
Run CREATE TABLE IF NOT EXISTS before inserting, or the statement fails with “no such table”.
tx
Must use a transaction
All executeSql calls belong inside db.transaction().
legacy
API may be missing
Feature-detect window.openDatabase. Modern Chrome has no Web SQL—point users to IndexedDB.
Compatibility
Browser Support
Web SQL is a deprecated client-side API. Firefox never shipped it. Chrome removed it (about Chrome 97+). Detect with window.openDatabase before running demos.
✓ Deprecated
Web SQL INSERT
Educational / legacy only. Prefer IndexedDB add() and put() for new production apps.
LegacyModern browser support
Google ChromeRemoved ~97+
Removed
Mozilla Firefox—
Never supported
Apple SafariLegacy
Historical / limited
Microsoft EdgeRemoved
Removed (Chromium)
OperaRemoved
Removed (Chromium)
Samsung InternetLegacy
Do not rely on
Internet Explorer—
Not supported
Web SQL INSERTDeprecated
Bottom line: For inserts in new web apps, use IndexedDB insert. Keep Web SQL only for maintaining old codebases.
Remember
Key Takeaways
Add a row:INSERT INTO t (cols) VALUES (?, ?) inside db.transaction().
New id: read result.insertId in the success callback.
Conflicts: use OR REPLACE or OR IGNORE instead of plain INSERT.
Legacy: Web SQL is deprecated—use IndexedDB for new projects.
One line: insert with INSERT INTO t (...) VALUES (?, ?) via executeSql, read insertId, prefer IndexedDB going forward.
Frequently Asked Questions
Open the database with openDatabase(), then call db.transaction(). Inside the transaction, use tx.executeSql() with INSERT INTO table (col1, col2) VALUES (?, ?). Pass column values as a bound parameter array instead of concatenating strings into the SQL.
Standard SQL: INSERT INTO table_name (col1, col2) VALUES (?, ?). Web SQL uses SQLite under the hood, so INSERT OR REPLACE and INSERT OR IGNORE are also supported for upserts and duplicate handling.
Read result.insertId in the success callback of executeSql. It holds the rowid SQLite assigned, which matches an INTEGER PRIMARY KEY column when your table uses one.
Web SQL requires all executeSql calls to run within db.transaction(). Transactions group operations atomically—if one statement fails, the whole transaction can roll back.
Web SQL is deprecated and removed from most modern browsers (including current Chrome). Treat this tutorial as educational for legacy code; use IndexedDB for new projects.
Plain INSERT fails if a unique constraint is violated. INSERT OR REPLACE deletes the existing row with the same key and inserts the new one. INSERT OR IGNORE silently skips the new row and keeps the original.