Logo

dev-resources.site

for different kinds of informations.

Make a real-time, offline first application with Instant

Published at
10/17/2024
Categories
webdev
nextjs
instantdb
realtime
Author
gyurmatag
Categories
4 categories in total
webdev
open
nextjs
open
instantdb
open
realtime
open
Author
9 person written this
gyurmatag
open
Make a real-time, offline first application with Instant

We all like to work with tools like Figma, Notion or Linear. These are all convenient, collaborative and real-time user interfaces in our hands, which make it a pleasure to work with.

You might think creating such a complex apps is only possible for million-dollar companies, but with InstantDB, a little AI magic and a little time to spare, we can get impressively close on our own.

In my previous article, I already presented how we can make a real-time chart component with Supabase and Tremor charts.

First, let's see what open source system our application will be built around? This is Instant.

Let's see what Instant is!

InstantDB is a modern, Firebase-like database solution designed for real-time, collaborative applications. It allows developers to manage relational queries directly in the frontend without needing to handle backend complexities like servers, sockets, or authentication. InstantDB supports features like optimistic updates, offline mode, and real-time sync, making it ideal for building responsive, multiplayer applications that work seamlessly even when offline.

So what are we going to build? Charli XCX's Brat album and the BratGenerator web interface they created for it, inspired me to do something similar.

It will look like this:

Image description

In terms of functions, this application has been further developed to the extent that the creations can be viewed, liked and we can even track these on a leaderboard.

Let's see how we can build this application! Before we go into the description of the codebase, you can find the entire project here on my Github.

Here is the X post from the app and the Github repo:

This was even shared by one of the creators of Instant. :)

By the way, you can access the live project here, deployed on Vercel.

Before cloning, it will be worthwhile to register on the Instant platform. You can do this here.
After registration, create a new application called realtime-brat-generator on the interface. We should see an interface like this at the top of the window:

Image description

Once we're done with that, let's clone the repo I linked above.
We can start this with the commands in the readme. Once we are done with that, the first thing to do is to rewrite the App ID to our own. This is found in the db.ts file and it references the .env file.

Then, after launch, we can already see that the application appears. But let's take a look at what exactly this app consists of.

If we go into package.json, we can see that the following technologies are used in the application:

  • Next.js
  • InstantDB
  • TailwindCSS
  • ShadCN
  • Eslint / Prettier

We can also look at the basics of the project. First, if we look at the db.ts file:

import { init } from '@instantdb/react';
import { Schema } from '@/lib/types';

export const APP_ID = process.env.APP_ID as string;

export const db = init<Schema>({ appId: APP_ID });
Enter fullscreen mode Exit fullscreen mode

Then we can see that here we define our database connection and our schema.

Our schema is in the types.ts file:

export type Schema = {
  bratCreations: BratCreation;
  votes: Vote;
};

export interface BratCreation {
  id: string;
  text: string;
  preset: string;
  createdAt: number;
  createdBy: string;
}

export interface Vote {
  id: string;
  createdUserId: string;
  createdAt: number;
  bratCreationId: string;
  orientation: 'upvote' | 'downvote';
}

export interface User {
  id: string;
  email: string;
}
Enter fullscreen mode Exit fullscreen mode

Simple enough, right? This database structure can also be seen on the Instant interface. Here in the following image:

Image description

On the Explorer page, we can see the current content of our database and how it is structured.

Let's go to page.tsx where we can see Instant in its full glory. This line of code queries the reactive data and we can use this hook as the useState() hook from React.

 const { isLoading, error, data } = db.useQuery({
    bratCreations: {},
    votes: {},
  });
Enter fullscreen mode Exit fullscreen mode

Here, we practically do not have to do anything with this, it will be immediately reactive and automatically update the interface if something changes in our data.
We can also perform filtering very easily here, you can read more about it here.

Let's take a look at the Instant specific points of interest in the same component:

if (existingVote) {
      if (existingVote.orientation === orientation) {
        db.transact(tx.votes[existingVote.id].delete());
      } else {
        db.transact(tx.votes[existingVote.id].update({ orientation }));
      }
    } else {
      db.transact(
        tx.votes[id()].update({
          createdUserId: user.id,
          createdAt: Date.now(),
          bratCreationId: creationId,
          orientation,
        })
      );
    }
  };

Enter fullscreen mode Exit fullscreen mode

Here you can see the code snippet where we can update and delete very easily with the Instant query language. The toggle operation of the liking system can be found here.

Another very interesting thing in the same file is authentication.

const { user } = db.useAuth();

Enter fullscreen mode Exit fullscreen mode

With this line, we can query our currently logged-in user. This will be important because of the liking function. Only logged in users will be able to like.

The entry itself is managed by these lines of code:

const handleSendMagicCode = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!authState.email) return;

    setAuthState({ ...authState, sentEmail: authState.email, error: null });

    try {
      await db.auth.sendMagicCode({ email: authState.email });
    } catch (error) {
      if (error instanceof Error) {
        setAuthState({ ...authState, error: error.message });
      }
    }
  };

  const handleVerifyMagicCode = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!authState.code) return;

    try {
      await db.auth.signInWithMagicCode({
        email: authState.sentEmail,
        code: authState.code,
      });
      setIsAuthModalOpen(false);
      setAuthState({ sentEmail: '', email: '', error: null, code: '' });
    } catch (error) {
      if (error instanceof Error) {
        setAuthState({ ...authState, error: error.message });
      }
    }
  };
Enter fullscreen mode Exit fullscreen mode

As you can see, we use email login and registration, which we can configure very easily on the appropriate interface of Instant, here:

Image description

However, for this we need to define Permissions in the application, on the Permissions tab in Instant DB. Here is the screen for that:

Image description

And also the code:

{
  "votes": {
    "bind": [
      "isOwner",
      "auth.id == data.createdUserId"
    ],
    "allow": {
      "create": "auth.id != null",
      "delete": "isOwner",
      "update": "isOwner"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In the permissions we must only "lock" the vote creations, because only authenticated users can create votes and of course only the vote creator (owner) can modify and delete these votes.

What's even more exciting is the code in BratCreationForm.tsx, where we can save our current Brat creation:

 const handleSave = () => {
    const bratCreation = {
      text: bratText,
      preset: selectedPreset.value,
      createdAt: Date.now(),
    };
    db.transact(
      tx.bratCreations[id()].update({
        ...bratCreation,
      })
    );

    setShowSaveAnimation(true);
    setShowConfetti(true);
    setConfettiComplete(false);

    setTimeout(() => {
      setShowSaveAnimation(false);
    }, 2000);
  };
Enter fullscreen mode Exit fullscreen mode

And with that, we practically covered all CRUD operations with Instant DB in our application.

Now let's test the application!

If you also feel like developing with InstantDB, feel free to fork this Github repo and add new features to it, make pull requests or start from scratch in a new application in a few minutes using this tutorial:

https://www.instantdb.com/docs

I hope this article was useful. If you have any questions, you can always find me here in the comment section or on X and we can talk! 🙂

I should also mention that this post was made possible thanks to ShiwaForce, where I’ve had the chance to work on projects like this and bring ideas to life.

realtime Article's
30 articles in total
Favicon
Real-Time Voice Interactions with the WebSocket Audio Adapter
Favicon
Curiosity: Using Ably.io Realtime Messaging as a Lightweight Database
Favicon
Real-Time Voice Interactions over WebRTC
Favicon
Building a Real-Time Collaborative Text Editor with Slate.js
Favicon
Chat API pricing: Comparing MAU and per-minute consumption models
Favicon
Scaling Kafka with WebSockets
Favicon
Build a Real-Time Voting System with Strapi & Instant DB: Part 2
Favicon
WebSocket architecture best practices: Designing scalable realtime systems
Favicon
Build a Real-Time Voting System with Strapi & Instant DB: Part 1
Favicon
Ingesting F1 Telemetry UDP real-time data in AWS EKS
Favicon
Make a real-time, offline first application with Instant
Favicon
Tennis Australia relies on Ably to deliver live experiences for millions of tennis fans worldwide
Favicon
OctoPalm.js || JavaScript library to add real-time, customizable search to your web applications.
Favicon
Building a "Real-Time" Data Integration Platform on AWS
Favicon
Implementing Real-Time Updates with Server-Sent Events (SSE) in C# .NET: A Complete Guide
Favicon
Understanding the Importance of Kafka in High-Volume Data Environments
Favicon
Building Real-Time Applications with SignalR in .NET
Favicon
Not All Market Research Studies Need to Have Real-Time/Live Data Reporting!
Favicon
Real-Time Capabilities in API Integration
Favicon
Migrate from Cord to SuperViz
Favicon
Harnessing Firebase in React with Custom Hooks: A Practical Guide
Favicon
laravel reverb installation process and setup with common mistakes
Favicon
🚀 Want to Boost Your Sports Development? Discover the Benefits of Real-Time Results with SportDevs!
Favicon
Authenticate Realtime Pub/Sub WebSocket clients with Supabase
Favicon
Reliably syncing database and frontend state: A realtime competitor analysis
Favicon
Webhooks: A Mindset Change for Batch Jobs
Favicon
Building a Real-Time Messaging Platform with Kafka
Favicon
Real-Time Data Handling with Firestore: Tracking Pending Orders
Favicon
System Design: Hybrid WebApp using server sent event
Favicon
Real-Time Irish Transit Analytics

Featured ones: