Skip to main content

How to Build an Offline Mobile App with React Native, Expo, and SQLite


In this article, we will discuss how to create an expense tracker mobile app using React Native, Expo, and a smartphone's local SQLite database. SQLite is similar to MySQL but is designed for mobile and embedded applications. Every smartphone has a local SQLite database that apps can use. However, locating these databases on your device can be tricky because they are stored within the app's sandbox, and each app has its own sandboxed storage.

In this guide, we will focus on an offline expense tracker that stores your expense data in a local SQLite database. For this example, we have named the database my.db. The app stores information such as the expense ID, day, date, expense amount, and any optional comments related to the expense.

Within the my.db database, we have created two tables: users and todos. The users table stores the id, day, date, and expense amount, while the todos table stores the comments. The two tables are linked using a foreign key, allowing the app to display comments associated with specific expenses, if any have been added.


Code for Creating a Database


import * as SQLite from 'expo-sqlite';

const db = SQLite.openDatabase('mydb.db'); 

export const initDB = () => {
  db.transaction(tx => {
    // Enable foreign key constraints
    tx.executeSql('PRAGMA foreign_keys = ON;');

    // Users table
    tx.executeSql(
      `CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        day TEXT,
        date TEXT,
        amount REAL
      );`
    );

    // Todos table (linked to users)
    tx.executeSql(
      `CREATE TABLE IF NOT EXISTS todos (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER,
        title TEXT,
        description TEXT,
        FOREIGN KEY(user_id) REFERENCES users(id)
      );`
    );
  });
};

export default db;

See how to render the SQLite data

import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ScrollView, Button } from 'react-native';
import * as SQLite from "expo-sqlite";

let db;
(async () => {
  db = await SQLite.openDatabaseAsync("mydb.db");
})();

const getUsersWithTodos = async (callback) => {
  if (!db) return;

  const users = await db.getAllAsync(`SELECT * FROM users;`);
  const todos = await db.getAllAsync(`SELECT * FROM todos;`);

  const usersWithTodos = users.map(user => ({
    ...user,
    todos: todos.filter(t => t.user_id === user.id),
  }));

  const totalAmount = users.reduce((sum, u) => sum + (u.amount || 0), 0);

  callback({ data: usersWithTodos, totalAmount });
};

const currencySymbols = {
  USD: '$',
  GBP: '£',
  EUR: '€',
  JPY: '¥',
  INR: '₹',
};

const UserList = ({ refreshFlag, currency }) => {
  const [usersData, setUsersData] = useState({ data: [], totalAmount: 0 });

  useEffect(() => {
    getUsersWithTodos(setUsersData);
  }, [refreshFlag]);

  const formatCurrency = (amount) =>
    `${currencySymbols[currency] || ''}${amount.toFixed(2)}`;

  const deleteUser = async (id) => {
    if (!db) return;

    await db.runAsync(`DELETE FROM users WHERE id = ?;`, [id]);
    await db.runAsync(`DELETE FROM todos WHERE user_id = ?;`, [id]);

    getUsersWithTodos(setUsersData);
  };

  return (
    
      Expenses
      
        Total Amount: {formatCurrency(usersData.totalAmount)}
      

      {usersData.data.map(user => (
        
          ID: {user.id}
          Day: {user.day}
          Date: {user.date}
          Amount: {formatCurrency(user.amount)}
          {user.todos.length > 0 ? (
            user.todos.map((t, idx) => (
              Comment: {t.title} - {t.description}
            ))
          ) : (
            No Comment
          )}
          
      ))}
    
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16, backgroundColor: '#fff' },
  title: { fontSize: 22, fontWeight: 'bold', marginBottom: 8 },
  total: {
    fontSize: 16,
    fontWeight: '600',
    marginBottom: 12,
    color: '#1e3a8a',
  },
  item: {
    backgroundColor: '#f3f4f6',
    padding: 12,
    borderRadius: 8,
    marginBottom: 10,
    borderWidth: 1,
    borderColor: '#d1d5db',
  },
});

export default UserList;
export { getUsersWithTodos, db };
 

How to Render Data from a Local SQLite Database

To retrieve and render data from the local SQLite database (mydb.db), we use simple SQL queries. For example:


const users = await db.getAllAsync(`SELECT * FROM users;`);
const todos = await db.getAllAsync(`SELECT * FROM todos;`);
    

These commands execute SQL SELECT statements to fetch all records from the users and todos tables, respectively.


Visit Github

Further Reading

  1.     


Contact Us

Name

Email *

Message *

Popular Posts

GMSK BER vs SNR (with Simulation)

  GMSK Performance: Why the BER vs. SNR Curve Matters If you’ve ever wondered why GMSK (Gaussian Minimum Shift Keying) became the backbone of the global GSM revolution, the answer lies in its unique balance of spectral efficiency and power performance. But there is no free lunch in engineering. To get that sleek, narrow spectrum, you have to pay a price in Bit Error Rate (BER) . In this guide, we break down the theoretical BER vs. SNR performance of GMSK, the "magic" of the BT product, and how to optimize your link budget for real-world conditions. What is GMSK and Why Should You Care? GMSK is a derivative of MSK (Minimum Shift Keying) that uses a Gaussian pre-modulation filter. This filter "smooths" the phase transitions, significantly reducing sideband power. The result? A constant envelope signal that allows you to use high-efficiency Class-C amplifiers without distorting the signal. This is the secr...

OFDM Symbols and Subcarriers Explained

This article explains how OFDM (Orthogonal Frequency Division Multiplexing) symbols and subcarriers work. It covers modulation, mapping symbols to subcarriers, subcarrier frequency spacing, IFFT synthesis, cyclic prefix, and transmission. Step 1: Modulation First, modulate the input bitstream. For example, with 16-QAM , each group of 4 bits maps to one QAM symbol. Suppose we generate a sequence of QAM symbols: s0, s1, s2, s3, s4, s5, …, s63 Step 2: Mapping Symbols to Subcarriers Assume N sub = 8 subcarriers. Each OFDM symbol in the frequency domain contains 8 QAM symbols (one per subcarrier): Mapping (example) OFDM symbol 1 → s0, s1, s2, s3, s4, s5, s6, s7 OFDM symbol 2 → s8, s9, s10, s11, s12, s13, s14, s15 … OFDM sym...

UGC NET Electronic Science Previous Year Question Papers with Solutions

Download Papers and Solutions Exam Pattern Preparation Tips FAQs More Home / Engineering & Other Exams / UGC NET 2026 PYQ 📊 Exam Highlights: Electronic Science (88) Feature Details Junior Research Fellowship (JRF) ₹37,000 + HRA per month Eligibility M.Sc/M.Tech in Electronics (55%) Validity of Certificate JRF (3 Years) | Lectureship (Lifetime) 📥 Download UGC NET Electronics PDFs Complete collection of previous year question papers, answer keys and explanations for Subject Code 88. Start Downloading 📂 View All Question Papers June 2026 - Question Paper Download PDF June 202...

Flat vs Frequency Selective Online Simulator

Flat vs Frequency Selective Online Simulator Channel Type Without Fading Flat Fading Multipaths Nakagami m SNR(dB) Run Simulation Input Signal Signal After Fading Constellation Diagram BER vs SNR Explore Advanced Flat vs Frequency-Selective Fading Simulator Want to see these equations in action? Visualize it. Launch Simulator Tool Flat Fading and Frequency-Selective Fading Effects of Multipath Components and the Nakagami-m Parameter 1. Flat Fading Flat fading occurs when the signal bandwidth is smaller than the channel's coherence bandwidth, meaning that all frequency components of the transmitted signal experience approximately the same fading. It is typically associated with narrowband signals, where the multipath delay spread is small relative to the symbol duration. ...

MATLAB Code for Constellation Diagram of QAM configurations such as 4, 8, 16, 32, 64, 128, and 256-QAM

📘 Overview of QAM 🧮 4-QAM MATLAB 🧮 16-QAM MATLAB 🚀 Online Simulator 📂 Other Topics on Constellation Diagrams... ▼ 🧮 MATLAB Code for 4-QAM 🧮 MATLAB Code for 16-QAM 🧮 MATLAB Code for m-ary QAM 🧮 Simulator for m-ary PSK 🧮 Simulator for m-ary QAM 🧮 Overview of Energy per Bit (Eb / N0) 🧮 Simulator for ASK, FSK, and PSK Overview of QAM One of the best-performing modulation techniques is QAM [↗] . Here, we modulate the symbols by varying the carrier signal's amplitude and phase in response to the variation in the message signal (or voltage variation). So, we may say that QAM is a combination of phase and amplitude modulation. Additionally, it performs better than ASK or PSK [↗] . In fact, any constellation for any type of modulatio...

MATLAB Code for ASK, FSK, and PSK (with Online Simulator)

MATLAB Code for ASK, FSK, and PSK Comprehensive implementation of digital modulation and demodulation techniques with simulation results. 📘 Theory 📡 ASK Code 📶 FSK Code 🎚️ PSK Code 🕹️ Simulator 📚 Further Reading Amplitude Shift Frequency Shift Phase Shift Live Simulator ASK, FSK & PSK HomePage MATLAB Code MATLAB Code for ASK Modulation and Demodulation COPY % The code is written by SalimWireless.Com clc; clear all; close all; % Parameters Tb = 1; fc = 10; N_bits = 10; Fs = 100 * fc; Ts = 1/Fs; samples_per_bit = Fs * Tb; rng(10); binar...

MATLAB code for BER vs SNR for M-QAM, M-PSK, QPSK, BPSK (with Simulation)

🧮 MATLAB Code for BPSK, M-ary PSK, and M-ary QAM Together 🧮 MATLAB Code for M-ary QAM 🧮 MATLAB Code for M-ary PSK 📚 Further Reading MATLAB Script for BER vs. SNR for M-QAM, M-PSK, QPSK, BPSK % Written by Salim Wireless clc; clear; close all; snr_db = -5:2:25; psk_orders = [2, 4, 8, 16, 32]; qam_orders = [4, 16, 64, 256]; ber_psk_results = zeros(length(psk_orders), length(snr_db)); ber_qam_results = zeros(length(qam_orders), length(snr_db)); for i = 1:length(psk_orders) ber_psk_results(i, :) = berawgn(snr_db, 'psk', psk_orders(i), 'nondiff'); end for i = 1:length(qam_orders) ber_qam_results(i, :) = berawgn(snr_db, 'qam', qam_orders(i)); end figure; semilogy(snr_db, ber_psk_results(1, :), 'o-', 'LineWidth', 1.5, 'DisplayName', 'BPSK'); hold on; for i = 2:length(psk_orders) semilogy(snr_db, ber_psk_results(i, :), 'o-', 'DisplayName', sprintf('%d-PSK', psk_or...