RORK LABJP
FUNDING — Rork raises a $15M seed led by Left Lane CapitalRORK MAX — Rork Max generates native Swift apps instead of React NativePLATFORM — It targets iPhone, iPad, Watch, and Vision Pro, reaching Live Activities and Core MLGROWTH — Traffic keeps climbing at 743K monthly visits and 85% growthTEST — The Companion app lets you test on a real device without a paid Apple Developer accountSTACK — Built on React Native and Expo for true native experiences, not web wrappersFUNDING — Rork raises a $15M seed led by Left Lane CapitalRORK MAX — Rork Max generates native Swift apps instead of React NativePLATFORM — It targets iPhone, iPad, Watch, and Vision Pro, reaching Live Activities and Core MLGROWTH — Traffic keeps climbing at 743K monthly visits and 85% growthTEST — The Companion app lets you test on a real device without a paid Apple Developer accountSTACK — Built on React Native and Expo for true native experiences, not web wrappers
Articles/Dev Tools
Dev Tools/2026-04-02Beginner

Building a Survey App with Rork — A Beginner's Guide to Forms, Data Collection, and Charts

Learn how to build a survey collection app from scratch using Rork. This beginner-friendly guide covers form design, multiple answer types, Supabase data storage, and aggregation charts — all in a single day.

surveyformRork480Supabase32React Native191beginner20data visualization2

Survey apps are incredibly practical — whether you're collecting customer feedback, running internal polls, or gathering event responses. Building a dedicated mobile app instead of relying on spreadsheets makes the experience far more pleasant for respondents, and gives you full control over the data.

In this guide, you'll build a fully functional survey app using Rork. We'll cover multiple question types (single choice, multiple choice, and free text), saving responses to Supabase, and displaying aggregated results as a bar chart. This is written for beginners — you don't need deep React Native knowledge, just a basic familiarity with Rork's interface.

What You'll Build

By the end of this tutorial, your app will include:

  • A survey form supporting single-choice, multiple-choice, and free-text questions
  • Response storage in Supabase via its REST API
  • An aggregation dashboard showing response counts and percentages as bar charts
  • Basic duplicate-response prevention using a device identifier

Prerequisites and Setup

Before diving in, make sure you have the following ready:

  • A Rork account (the free plan is sufficient for this project)
  • A Supabase account (free tier works fine — you'll need the project URL and anon key)
  • A rough idea of your survey questions, options, and expected answer types

If you haven't created a Supabase project yet, go ahead and do that first. The setup only takes a couple of minutes.

App Architecture Overview

Screen Structure

The app consists of three screens:

  • Home screen — Displays the survey title, description, and a "Start Survey" button
  • Form screen — Shows questions one at a time in a step-by-step flow
  • Dashboard screen — Visualizes aggregated responses with bar charts

Database Schema

You'll need two tables in Supabase:

surveys
  id          uuid (primary key)
  title       text
  description text
  created_at  timestamp

responses
  id            uuid (primary key)
  survey_id     uuid (foreign key → surveys)
  question_id   text
  answer        text
  respondent_id text
  created_at    timestamp

The answer field stores a plain string for single-choice and free text, and a comma-separated string for multiple-choice responses. This keeps the schema simple while covering all common cases.

Step-by-Step Implementation

Step 1: Set Up Supabase Tables

Open the SQL editor in your Supabase dashboard and run the following:

-- Create the surveys table
CREATE TABLE surveys (
  id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  title text NOT NULL,
  description text,
  created_at timestamp DEFAULT now()
);
 
-- Create the responses table
CREATE TABLE responses (
  id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  survey_id uuid REFERENCES surveys(id) ON DELETE CASCADE,
  question_id text NOT NULL,
  answer text NOT NULL,
  respondent_id text,
  created_at timestamp DEFAULT now()
);
 
-- Enable Row-Level Security and allow public access for this demo
ALTER TABLE surveys ENABLE ROW LEVEL SECURITY;
ALTER TABLE responses ENABLE ROW LEVEL SECURITY;
 
CREATE POLICY "Public surveys are viewable" ON surveys FOR SELECT USING (true);
CREATE POLICY "Anyone can submit responses" ON responses FOR INSERT WITH CHECK (true);
CREATE POLICY "Responses are viewable" ON responses FOR SELECT USING (true);

Once the tables are created, copy your project URL and anon key from Settings → API.

Step 2: Create the Project in Rork

Open Rork, create a new project, and use this prompt to get started:

Build a survey collection app with the following features:

1. A home screen showing the survey title, description, and a "Start Survey" button
2. A step-by-step form screen that shows one question at a time
3. Three question types: single choice (radio buttons), multiple choice (checkboxes),
   and free text (text input)
4. A "Submit" button at the end that saves responses to Supabase
5. A dashboard screen showing response counts and percentage bars per option

Use a clean, blue-themed color scheme.

Rork will generate the screen structure automatically. Review the UI and refine as needed before wiring up the data layer.

Step 3: Define Your Survey Questions

Add a questions.ts file to store your question data:

// questions.ts — Survey question definitions
export const SURVEY_QUESTIONS = [
  {
    id: "q1",
    type: "single", // single-choice
    text: "How would you describe your app development experience?",
    options: ["No experience", "Self-teaching", "Published personal apps", "Professional developer"],
  },
  {
    id: "q2",
    type: "multiple", // multiple-choice
    text: "Which no-code/AI app builders have you tried? (Select all that apply)",
    options: ["Rork", "FlutterFlow", "Adalo", "Bubble", "Bolt", "None"],
  },
  {
    id: "q3",
    type: "text", // free-text
    text: "What's your biggest challenge when building apps?",
  },
];

Step 4: Save Responses to Supabase

Set up the Supabase client and a submit function:

// supabase.ts — Supabase client and response submission
import { createClient } from "@supabase/supabase-js";
 
const supabase = createClient(
  process.env.EXPO_PUBLIC_SUPABASE_URL!,
  process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!
);
 
export async function submitSurveyResponses(
  surveyId: string,
  answers: Record<string, string | string[]>,
  respondentId: string
) {
  const rows = Object.entries(answers).map(([questionId, answer]) => ({
    survey_id: surveyId,
    question_id: questionId,
    // Join arrays (multiple choice) into a comma-separated string
    answer: Array.isArray(answer) ? answer.join(",") : answer,
    respondent_id: respondentId,
  }));
 
  const { error } = await supabase.from("responses").insert(rows);
 
  if (error) {
    console.error("Failed to save responses:", error.message);
    throw error;
  }
 
  return { success: true };
}

Use a device-generated UUID stored in AsyncStorage as the respondentId to prevent duplicate submissions from the same device.

Ask Rork to wire up the submit button with this prompt:

When the user taps Submit, call submitSurveyResponses() with the collected answers
and respondentId. Show a loading spinner during submission. On success, navigate to
a "Thank You" screen. On error, show an alert with the error message.

Step 5: Build the Results Dashboard

Fetch and aggregate response data with a custom hook:

// useResponseStats.ts
import { useEffect, useState } from "react";
import { supabase } from "./supabase";
 
interface AnswerStat {
  option: string;
  count: number;
  percentage: number;
}
 
export function useResponseStats(surveyId: string, questionId: string) {
  const [stats, setStats] = useState<AnswerStat[]>([]);
  const [total, setTotal] = useState(0);
 
  useEffect(() => {
    async function fetchStats() {
      const { data } = await supabase
        .from("responses")
        .select("answer")
        .eq("survey_id", surveyId)
        .eq("question_id", questionId);
 
      if (!data) return;
 
      const countMap: Record<string, number> = {};
      data.forEach(({ answer }) => {
        answer.split(",").forEach((opt: string) => {
          const key = opt.trim();
          countMap[key] = (countMap[key] || 0) + 1;
        });
      });
 
      const totalCount = data.length;
      setTotal(totalCount);
      setStats(
        Object.entries(countMap).map(([option, count]) => ({
          option,
          count,
          percentage: Math.round((count / totalCount) * 100),
        }))
      );
    }
    fetchStats();
  }, [surveyId, questionId]);
 
  return { stats, total };
}

For the charting layer, Building Interactive Charts in Your Rork App — Victory Native Guide walks through exactly how to render bar and pie charts from this kind of aggregated data.

Common Errors and Fixes

"Row-level security policy violation" — This means the RLS policies weren't applied correctly. Re-run the CREATE POLICY SQL statements from Step 1 and confirm they're listed under Authentication → Policies in the Supabase dashboard.

"Cannot read property of undefined" — This usually means the component rendered before data was ready. Make sure your hooks provide sensible default values ([] for arrays, 0 for counts) and show a loading indicator while data is being fetched.

Duplicate responses being saved — Double-check that your respondentId is being generated once and persisted in AsyncStorage. If it's re-generated on every app launch, the same user will appear as a new respondent each time.

Ideas for Extending the App

Once the core app is working, here are some directions worth exploring:

  • Dynamic question management: Move question data to a Supabase table so you can edit surveys without redeploying the app
  • Submission deadline: Add an expires_at field to limit when responses can be submitted
  • CSV export: Let admins download response data as a spreadsheet — this is highly valued in business settings
  • SaaS expansion: Turn it into a multi-survey platform with user accounts, and charge a monthly subscription fee. For building real-time dashboards on top of Supabase, Building a Real-Time Analytics Dashboard with Rork Max and Supabase Realtime is a great next read.

Wrapping Up

Building a survey app with Rork is a great way to get hands-on experience with forms, data persistence, and data visualization all in one project. Here's a quick recap of what we covered:

  • Setting up Supabase tables with RLS policies for public surveys
  • Generating the form UI in Rork and connecting it to your question definitions
  • Saving responses to Supabase and preventing duplicate submissions
  • Fetching aggregated data and rendering it as a dashboard

The survey app you built today is a solid foundation — take it further by adding dynamic question management, deadlines, and eventually a full SaaS model with authentication and billing.

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-06-23
DAU Went Up but Retention Didn't — Rebuilding Gamification That Actually Sticks in Rork Apps
Points, badges, and leaderboards lift DAU, but retention is a different story. Field notes on a server-authoritative point ledger, streaks that forgive, and leaderboards that don't crush newcomers — with working code for Rork apps.
Dev Tools2026-05-03
react-native-chart-kit BarChart with Multiple Datasets: Side-by-Side Implementation Patterns for Rork Apps
react-native-chart-kit's BarChart doesn't support multiple datasets out of the box. Learn 3 practical workarounds for side-by-side grouped bar charts in Rork apps — from quick color-coded fixes to migrating to Victory Native.
Dev Tools2026-04-19
Offline-First Architecture in Rork Apps: WatermelonDB + Supabase Sync
A complete guide to implementing offline-first architecture in Rork apps using WatermelonDB and Supabase Realtime. Covers local caching, optimistic updates, conflict resolution, and cross-device sync.
📚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 →