RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-04-04Intermediate

Building a Real-Time Sync App with Rork × Firebase Realtime Database: A Beginner's Guide

Learn how to integrate Firebase Realtime Database into your Rork app from scratch. This guide covers real-time data sync, CRUD operations, offline support, and security rules — with working code examples throughout.

Firebase10Realtime Databasereal-time4data syncRork515backend9React Native209

What is Firebase Realtime Database — and Why Does It Work So Well with Rork?

Imagine building an app where every user sees the same data update in real time, without refreshing. Chat apps, live scoreboards, collaborative tools, inventory management — there are countless use cases where multiple users need to share data instantly.

Firebase Realtime Database is a cloud-hosted NoSQL database from Google. Its defining feature is real-time data synchronization: changes are pushed to every connected client in milliseconds via a persistent WebSocket connection, with no polling required.

Because Rork generates apps using React Native (Expo), the Firebase JavaScript SDK integrates smoothly. You can even use Rork prompts to generate and refine your Firebase integration code, making it accessible even if you've never used Firebase before.

Setting Up Your Firebase Project

Create a Project in the Firebase Console

  1. Go to the Firebase console and click Add project.
  2. Enter a project name (e.g., my-rork-app), configure Google Analytics if desired, and click Create project.
  3. In the left sidebar, go to BuildRealtime Database.
  4. Click Create database, select a region (for Japan-based users, asia-southeast1 or us-central1 works well), and choose Test mode for the security rules during development (you'll update these before production).

Register Your iOS and Android Apps

  1. From your Firebase project overview, click Add app and select iOS or Android.
  2. iOS: Enter your bundle ID (e.g., com.yourname.myapp) and download GoogleService-Info.plist.
  3. Android: Enter your package name (e.g., com.yourname.myapp) and download google-services.json.
  4. Place these files in the appropriate location within your Rork project (usually the root or an assets/ folder).

Adding the Firebase SDK to Your Rork App

Installing the Required Packages

You can ask Rork to handle this step with a prompt like:

Please install the Firebase packages needed for Realtime Database and set up the
initialization code. Read config values from environment variables.
Required packages: firebase (Web SDK)

Or install manually in your Expo project:

npx expo install firebase

Initializing Firebase (firebase.ts)

Create a firebase.ts file (or src/lib/firebase.ts) in your project:

// src/lib/firebase.ts
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getDatabase } from 'firebase/database';
 
// Config values from Firebase Console → Project Settings → Your apps
const firebaseConfig = {
  apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.EXPO_PUBLIC_FIREBASE_DATABASE_URL, // required
  projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID,
};
 
// Guard against double initialization
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();
const database = getDatabase(app);
 
export { app, database };

Store your config values in a .env.local file and make sure it's listed in .gitignore. Never hardcode API keys in your source files — use placeholders like YOUR_API_KEY or environment variables at all times.

Reading and Writing Data: CRUD Operations

Firebase Realtime Database stores everything as a JSON tree. You reference data by path (e.g., /todos/item1) to read from or write to specific locations.

Writing Data (set / push)

// src/hooks/useFirebaseData.ts
import { database } from '../lib/firebase';
import { ref, set, push, remove, update } from 'firebase/database';
 
// Overwrite data at a specific path
export const setData = async (path: string, data: object) => {
  const dataRef = ref(database, path);
  await set(dataRef, data);
};
 
// Add a new item to a list (Firebase auto-generates a unique ID)
export const addItem = async (path: string, data: object) => {
  const listRef = ref(database, path);
  const newItemRef = push(listRef);
  await set(newItemRef, {
    ...data,
    createdAt: Date.now(),
  });
  return newItemRef.key;
};
 
// Delete an item
export const deleteItem = async (path: string) => {
  const itemRef = ref(database, path);
  await remove(itemRef);
};
 
// Partially update an item
export const updateItem = async (path: string, updates: object) => {
  const itemRef = ref(database, path);
  await update(itemRef, updates);
};

Subscribing to Real-Time Changes with onValue

The onValue listener is the heart of Firebase Realtime Database. Any time the data at a given path changes — whether from your device or another user's — the callback fires and your UI updates automatically.

// src/hooks/useRealtimeList.ts
import { useEffect, useState } from 'react';
import { database } from '../lib/firebase';
import { ref, onValue, off } from 'firebase/database';
 
interface Item {
  id: string;
  text: string;
  completed: boolean;
  createdAt: number;
}
 
export const useRealtimeList = (path: string) => {
  const [items, setItems] = useState<Item[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
 
  useEffect(() => {
    const dataRef = ref(database, path);
 
    const unsubscribe = onValue(
      dataRef,
      (snapshot) => {
        const data = snapshot.val();
        if (data) {
          // Firebase returns an object — convert it to an array
          const list = Object.entries(data).map(([id, value]) => ({
            id,
            ...(value as Omit<Item, 'id'>),
          }));
          list.sort((a, b) => a.createdAt - b.createdAt);
          setItems(list);
        } else {
          setItems([]);
        }
        setLoading(false);
      },
      (err) => {
        setError(err.message);
        setLoading(false);
      }
    );
 
    // Clean up listener when the component unmounts
    return () => off(dataRef);
  }, [path]);
 
  return { items, loading, error };
};

A Real-Time TODO List Component

Here's a working component built with the hooks above:

// src/components/RealtimeTodoList.tsx
import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, FlatList, StyleSheet } from 'react-native';
import { useRealtimeList } from '../hooks/useRealtimeList';
import { addItem, deleteItem, updateItem } from '../hooks/useFirebaseData';
 
const TODO_PATH = 'todos';
 
export const RealtimeTodoList: React.FC = () => {
  const [inputText, setInputText] = useState('');
  const { items, loading } = useRealtimeList(TODO_PATH);
 
  const handleAdd = async () => {
    if (!inputText.trim()) return;
    await addItem(TODO_PATH, { text: inputText.trim(), completed: false });
    setInputText('');
  };
 
  const handleToggle = async (id: string, completed: boolean) => {
    await updateItem(`${TODO_PATH}/${id}`, { completed: !completed });
  };
 
  const handleDelete = async (id: string) => {
    await deleteItem(`${TODO_PATH}/${id}`);
  };
 
  if (loading) return <Text>Loading...</Text>;
 
  return (
    <View>
      <View style={{ flexDirection: 'row' }}>
        <TextInput
          value={inputText}
          onChangeText={setInputText}
          placeholder="Add a task..."
          style={{ flex: 1, borderWidth: 1, padding: 8 }}
        />
        <TouchableOpacity onPress={handleAdd} style={{ padding: 8, backgroundColor: '#4CAF50' }}>
          <Text style={{ color: '#fff' }}>Add</Text>
        </TouchableOpacity>
      </View>
      <FlatList
        data={items}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 8 }}>
            <TouchableOpacity onPress={() => handleToggle(item.id, item.completed)}>
              <Text style={{ textDecorationLine: item.completed ? 'line-through' : 'none' }}>
                {item.completed ? '✅ ' : '⬜ '}{item.text}
              </Text>
            </TouchableOpacity>
            <TouchableOpacity onPress={() => handleDelete(item.id)}>
              <Text>🗑</Text>
            </TouchableOpacity>
          </View>
        )}
      />
    </View>
  );
};

Open this component on two devices simultaneously and watch what happens — add a task on one device and it appears instantly on the other. No refresh, no polling. That's the magic of Firebase Realtime Database.

Security Rules: Essential Before Going Live

Test mode disables security entirely. Before releasing your app, update the rules in the Firebase console:

// Firebase Console → Realtime Database → Rules
{
  "rules": {
    "todos": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid",
        ".write": "auth != null && auth.uid == $uid"
      }
    }
  }
}

This ensures each user can only access their own data. For a full guide on pairing Firebase with user authentication in Rork, see Implementing User Authentication in Rork — Firebase & Supabase Guide.

Enabling Offline Support

Firebase Realtime Database caches data locally and syncs changes when the connection is restored. You can also monitor the connection state using the special .info/connected path:

// Monitor real-time connection status
import { ref, onValue } from 'firebase/database';
 
export const monitorConnection = (
  onConnected: () => void,
  onDisconnected: () => void
) => {
  const connectedRef = ref(database, '.info/connected');
  onValue(connectedRef, (snap) => {
    if (snap.val() === true) {
      onConnected();
    } else {
      onDisconnected();
    }
  });
};

Writes made while offline are queued locally and automatically flushed to Firebase when connectivity returns — a great user experience for mobile apps with unreliable network conditions.

For a broader look at backend integration patterns in Rork, check out the Rork External API Integration Guide.

Looking back

In this guide, we covered how to integrate Firebase Realtime Database into a Rork app from start to finish — setting up the Firebase project, initializing the SDK with environment variables, writing real-time listeners with onValue, implementing CRUD operations, configuring security rules, and enabling offline support.

The most exciting part of working with Firebase Realtime Database is experiencing data changes flow across devices in milliseconds. The code to achieve this is surprisingly concise, and Rork's prompt-based workflow makes it even faster to build on top of these patterns.

If you're ready to take things further — building a full real-time chat app with authentication, presence detection, and push notifications — Building a Real-Time Chat App in Rork — Supabase Auth, Realtime DB & Push Notifications is an excellent next step. It uses a different backend (Supabase) but the architectural lessons apply equally well.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-04-07
Rork Max × Liveblocks / Yjs: Real-Time Collaborative App Development
A complete guide to integrating Liveblocks and Yjs into Rork Max apps for real-time collaborative editing. From CRDT fundamentals to production deployment, everything you need to build multi-user apps.
Dev Tools2026-07-15
The Comma That Fell to the Start of a Line: Rork, Quote Cards, and Japanese Line Breaking
React Native's Text component does not guarantee Japanese line-breaking rules. Here are the violation rates I measured, a WORD JOINER implementation that survives copy and VoiceOver, an onTextLayout audit harness, and how Rork Max (SwiftUI) differs.
Dev Tools2026-07-15
My Rork sleep timer faded out on time — but the sound didn't
Rork writes sleep-timer fades against the wall clock with setInterval. The numbers say 30 minutes, but the real audio fades early or cuts out abruptly. Here is how to drive the fade from the actual playback position, why a logarithmic curve sounds natural, and the honest limit of JS fades when the screen is locked.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →