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/Getting Started
Getting Started/2026-04-05Intermediate

Build a Matching App with Rork: Implementing Swipe UI and Chat Features

Build a matching app with Rork: swipe UI, Supabase match detection, and real-time chat. A step-by-step tutorial for intermediate React Native developers.

matching appswipe UISupabase33React Native209tutorial20

Dating and matching apps are one of the most in-demand app categories today. Swipe interactions, match notifications, and real-time chat — building all of this from scratch can seem overwhelming. But with Rork, even developers with limited coding experience can put together the core features of a matching app.

Below, we build a matching app with Rork step by step, from designing the swipe UI to wiring up the chat experience.

What We're Building

  • A swipe UI with profile cards (swipe right = like, swipe left = pass)
  • Match detection when two users mutually like each other
  • Real-time chat between matched users (powered by Supabase Realtime)
  • Profile photo upload (Supabase Storage)

Prerequisites

To follow this tutorial, you'll need:

  • A Rork account (the free plan works fine to get started)
  • A Supabase account (has a generous free tier)
  • A basic understanding of React Native (helpful but not required)

If you're new to Rork, check out Getting Started with Rork before diving in.

Setting Up Supabase

You'll need four tables in your Supabase database:

  • profiles — user data (name, age, bio, avatar URL)
  • swipes — records of who liked or passed on whom
  • matches — pairs who mutually liked each other
  • messages — chat messages between matched users

Run the following SQL in the Supabase SQL Editor:

-- User profiles
create table profiles (
  id uuid references auth.users primary key,
  name text not null,
  age integer,
  bio text,
  avatar_url text,
  created_at timestamptz default now()
);
 
-- Swipe records
create table swipes (
  id uuid default gen_random_uuid() primary key,
  swiper_id uuid references profiles(id),
  swiped_id uuid references profiles(id),
  direction text check (direction in ('like', 'pass')),
  created_at timestamptz default now(),
  unique(swiper_id, swiped_id)
);
 
-- Match records
create table matches (
  id uuid default gen_random_uuid() primary key,
  user1_id uuid references profiles(id),
  user2_id uuid references profiles(id),
  created_at timestamptz default now()
);
 
-- Chat messages
create table messages (
  id uuid default gen_random_uuid() primary key,
  match_id uuid references matches(id),
  sender_id uuid references profiles(id),
  body text not null,
  created_at timestamptz default now()
);
 
-- Enable Realtime for messages
alter publication supabase_realtime add table messages;

After running this SQL, set up Row Level Security (RLS) policies in Supabase under Authentication → Policies to ensure users can only access their own data.

Implementing the Swipe UI

Create your project in Rork and enter a prompt to generate the swipe screen. Rork will produce React Native code from your description.

Example Rork Prompt

Build a swipe screen for a dating/matching app.
- Show a profile card in the center of the screen with photo, name, age, and bio
- Swipe right to like, swipe left to pass
- Use React Native Gesture Handler for smooth animations
- Add "❌ Pass" and "❤️ Like" buttons below the card as well
- Fetch profile data from a Supabase table called profiles

The code Rork generates will look something like this:

// screens/SwipeScreen.tsx (Rork-generated example)
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, {
  useAnimatedGestureHandler,
  useAnimatedStyle,
  useSharedValue,
  withSpring,
  runOnJS,
} from 'react-native-reanimated';
 
export function SwipeScreen() {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
 
  const gestureHandler = useAnimatedGestureHandler({
    onActive: (event) => {
      translateX.value = event.translationX;
      translateY.value = event.translationY;
    },
    onEnd: (event) => {
      if (event.translationX > 100) {
        // Swiped right — like
        runOnJS(handleLike)();
      } else if (event.translationX < -100) {
        // Swiped left — pass
        runOnJS(handlePass)();
      }
      translateX.value = withSpring(0);
      translateY.value = withSpring(0);
    },
  });
 
  return (
    <PanGestureHandler onGestureEvent={gestureHandler}>
      <Animated.View style={[styles.card, animatedStyle]}>
        {/* Profile card content here */}
      </Animated.View>
    </PanGestureHandler>
  );
}

Match Detection Logic

After each swipe, record the interaction in Supabase and check for a mutual like:

// utils/swipe.ts
import { supabase } from './supabase';
 
export async function handleSwipe(
  swiperId: string,
  swipedId: string,
  direction: 'like' | 'pass'
) {
  // Record the swipe
  await supabase.from('swipes').insert({
    swiper_id: swiperId,
    swiped_id: swipedId,
    direction,
  });
 
  if (direction === 'like') {
    // Check if the other user already liked back
    const { data } = await supabase
      .from('swipes')
      .select('id')
      .eq('swiper_id', swipedId)
      .eq('swiped_id', swiperId)
      .eq('direction', 'like')
      .single();
 
    if (data) {
      // Mutual like — it's a match!
      await supabase.from('matches').insert({
        user1_id: swiperId,
        user2_id: swipedId,
      });
      return { matched: true };
    }
  }
 
  return { matched: false };
}

When a match occurs, show a celebratory modal and navigate the user to the chat screen.

Real-Time Chat

Once two users have matched, they can start chatting. Supabase Realtime makes it easy to deliver messages instantly to both parties without polling.

// screens/ChatScreen.tsx
import { useEffect, useState } from 'react';
import { supabase } from '../utils/supabase';
 
export function ChatScreen({ matchId }: { matchId: string }) {
  const [messages, setMessages] = useState([]);
 
  useEffect(() => {
    // Load existing messages
    fetchMessages();
 
    // Subscribe to new messages in real time
    const channel = supabase
      .channel(`match:${matchId}`)
      .on(
        'postgres_changes',
        {
          event: 'INSERT',
          schema: 'public',
          table: 'messages',
          filter: `match_id=eq.${matchId}`,
        },
        (payload) => {
          setMessages((prev) => [...prev, payload.new]);
        }
      )
      .subscribe();
 
    return () => supabase.removeChannel(channel);
  }, [matchId]);
 
  const sendMessage = async (body: string) => {
    await supabase.from('messages').insert({
      match_id: matchId,
      sender_id: currentUserId,
      body,
    });
    // Realtime handles delivery to the other user automatically
  };
 
  return (
    // Chat UI
  );
}

For a deeper dive into real-time chat architecture, check out Building a Real-Time Chat App with Rork and Supabase.

Profile Photo Upload

Letting users upload their own photos is essential for any matching app. Here's how to handle it with Supabase Storage:

// utils/uploadAvatar.ts
import * as ImagePicker from 'expo-image-picker';
import { supabase } from './supabase';
import { decode } from 'base64-arraybuffer';
 
export async function uploadAvatar(userId: string): Promise<string | null> {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [1, 1], // Square crop for profile photos
    quality: 0.8,
    base64: true,
  });
 
  if (result.canceled || !result.assets[0].base64) return null;
 
  const fileName = `avatars/${userId}-${Date.now()}.jpg`;
 
  const { error } = await supabase.storage
    .from('profiles')
    .upload(fileName, decode(result.assets[0].base64), {
      contentType: 'image/jpeg',
      upsert: true,
    });
 
  if (error) throw error;
 
  const { data } = supabase.storage.from('profiles').getPublicUrl(fileName);
  return data.publicUrl;
}

Common Errors and Fixes

RLS policy error (403 Forbidden)

If you see "permission denied for table swipes" or similar errors, your Row Level Security policies are missing or misconfigured. In the Supabase dashboard under Authentication → Policies, add policies that allow authenticated users to read and write their own rows.

Swipe animation feels choppy

Check how you're using runOnUI vs runOnJS in Reanimated. Heavy work (like database calls) belongs on the JS thread, while animation values should stay on the UI thread. Mixing them up causes jank.

Supabase Realtime not firing

Double-check that you ran alter publication supabase_realtime add table messages;. Without this step, the Realtime subscription won't receive any events.

Looking back

With Rork, you can build the core pillars of a matching app — swipe interactions, match detection, and real-time chat — without having to wire everything together from scratch. The key is to nail your Supabase data model first, then use Rork's prompting workflow to generate and iterate on the UI.

Matching apps have plenty of room to grow: push notifications, location-based filtering, premium subscriptions, and more. Start with an MVP, ship it, and evolve based on how real users engage with it.

For a comprehensive look at building and monetizing apps as an indie developer, React Native Expo Architecture Guide is a great next read.

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

Getting Started2026-04-19
Build a Medication Tracker App with Rork — Never Miss a Dose Again
Learn how to build a medication tracker app with Rork from scratch. This practical guide covers drug registration, daily check-ins, reminder notifications, and history tracking — no coding required.
Getting Started2026-04-19
Build a Travel Planner App with Rork — Destinations, Schedules, and Packing Lists in One
A hands-on tutorial for building a travel planner app with Rork. Learn how to combine destination management, day-by-day itineraries, and packing checklists into a single app using prompts.
Getting Started2026-03-31
Build a Sleep Tracker App with Rork — A Complete Tutorial from Bedtime Logging to Sleep Score Analysis
Build a sleep tracker app with Rork. Log bedtimes, calculate sleep scores, and visualize weekly trends — a complete beginner tutorial with step-by-step prompts.
📚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 →