RORK LABJP
PLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updatesPLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updates
Articles/Getting Started
Getting Started/2026-03-24Beginner

Build a Calendar & Schedule App with Rork — A Complete Beginner's Tutorial

Learn how to build a fully functional calendar and schedule management app from scratch using Rork. This step-by-step tutorial covers calendar UI, event CRUD, local storage, and reminder notifications.

Rork547Calendar AppSchedule ManagementTutorial5Beginner7React Native234Expo193

Setup and context — Learn by Building a Calendar App

Schedule management apps are among the most universally useful types of mobile applications. By building one, you'll naturally learn key app development concepts like state management, data persistence, UI component design, and user interaction patterns — all in a single project.

Below, we build a calendar and schedule management app from scratch using Rork. Even with minimal coding experience, you can lean on Rork's AI assistant to turn well-crafted prompts into production-ready code, freeing you to focus on design and functionality decisions.

What you'll learn:

  • Building a month-view calendar UI with event markers
  • Managing event data (create, read, update, delete)
  • Persisting data locally with AsyncStorage
  • Adding reminder notifications with Expo Notifications
  • Polishing the UI to App Store / Google Play standards

If you're brand new to Rork, we recommend starting with "Getting Started with Rork" to get familiar with the basics.


Prerequisites & Setup

What You'll Need

  • A Rork account (the free plan covers everything in this tutorial)
  • A smartphone (iOS or Android — for previewing with Expo Go)
  • A web browser on your computer (to access the Rork editor)

Helpful Background Knowledge

  • Basic understanding of HTML/CSS
  • Familiarity with JavaScript variables, functions, and arrays

That said, Rork's AI handles the heavy lifting, so you can follow along even without deep programming knowledge. Whenever something is unclear, just ask Rork's chat — it'll explain any code it generates.


Step 1: Create Your Project

Log into the Rork dashboard and click "New Project." Name it something descriptive like my-calendar-app.

For your initial prompt, try something like this:

Build a calendar and schedule management app with these features:
1. Month-view calendar UI (highlight today's date)
2. Tap a date to see that day's events
3. Event creation form (title, start time, end time, notes)
4. Edit and delete events
5. Save data locally with AsyncStorage
6. Modern, clean design with a blue color theme

Rork will generate the screen layout, components, and navigation structure based on this prompt. After a few minutes, you'll see a preview — scan the QR code with Expo Go to test it on your device.


Step 2: Build the Calendar UI

Rork typically generates calendar components using the react-native-calendars library. Here's what the core calendar component looks like with event dot markers:

// CalendarScreen.tsx — Main calendar screen
import React, { useState, useMemo } from 'react';
import { View, StyleSheet } from 'react-native';
import { Calendar } from 'react-native-calendars';
 
type Event = {
  id: string;
  title: string;
  date: string; // 'YYYY-MM-DD'
  startTime: string;
  endTime: string;
  memo: string;
};
 
type Props = {
  events: Event[];
  onDayPress: (dateString: string) => void;
};
 
export default function CalendarScreen({ events, onDayPress }: Props) {
  const [selectedDate, setSelectedDate] = useState('');
 
  // Add dot markers to dates that have events
  const markedDates = useMemo(() => {
    const marks: Record<string, any> = {};
    events.forEach((event) => {
      marks[event.date] = {
        ...marks[event.date],
        marked: true,
        dotColor: '#4A90D9',
      };
    });
    if (selectedDate) {
      marks[selectedDate] = {
        ...marks[selectedDate],
        selected: true,
        selectedColor: '#4A90D9',
      };
    }
    return marks;
  }, [events, selectedDate]);
 
  const handleDayPress = (day: { dateString: string }) => {
    setSelectedDate(day.dateString);
    onDayPress(day.dateString);
  };
 
  return (
    <View style={styles.container}>
      <Calendar
        markedDates={markedDates}
        onDayPress={handleDayPress}
        theme={{
          todayTextColor: '#4A90D9',
          arrowColor: '#4A90D9',
          monthTextColor: '#333',
          textDayFontSize: 16,
          textMonthFontSize: 18,
          textDayHeaderFontSize: 14,
        }}
      />
    </View>
  );
}
 
// Expected output:
// A month-view calendar is displayed with blue dots on dates that have events.
// Tapping a date highlights it (blue background) and shows that day's events below.
 
const styles = StyleSheet.create({
  container: {
    backgroundColor: '#FFFFFF',
    borderRadius: 12,
    overflow: 'hidden',
    margin: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
});

The key detail here is using useMemo to efficiently compute markedDates. Whenever the event data changes, the calendar markers update automatically without unnecessary re-renders.


Step 3: Build the Event Creation Form

Next, let's implement the form for adding events. Give Rork a prompt like this:

Create an event form with:
- Title (required, text input)
- Date (selected via DateTimePicker)
- Start time and end time (TimePicker)
- Notes (optional, multiline text)
- Save and Cancel buttons
- Validation: show error if title is empty

Here's what the core form component typically looks like:

// AddEventForm.tsx — Event creation form (core logic)
import React, { useState } from 'react';
import {
  View, Text, TextInput, TouchableOpacity,
  Alert, StyleSheet, ScrollView,
} from 'react-native';
 
type EventInput = {
  title: string;
  date: string;
  startTime: string;
  endTime: string;
  memo: string;
};
 
type Props = {
  initialDate: string;
  onSave: (event: EventInput) => void;
  onCancel: () => void;
};
 
export default function AddEventForm({ initialDate, onSave, onCancel }: Props) {
  const [title, setTitle] = useState('');
  const [memo, setMemo] = useState('');
  const [startTime, setStartTime] = useState('09:00');
  const [endTime, setEndTime] = useState('10:00');
 
  const handleSave = () => {
    if (!title.trim()) {
      Alert.alert('Validation Error', 'Please enter a title for this event');
      return;
    }
    onSave({
      title: title.trim(),
      date: initialDate,
      startTime,
      endTime,
      memo: memo.trim(),
    });
  };
 
  return (
    <ScrollView style={styles.container}>
      <Text style={styles.label}>Title *</Text>
      <TextInput
        style={styles.input}
        value={title}
        onChangeText={setTitle}
        placeholder="Meeting, lunch, study group..."
      />
 
      <Text style={styles.label}>Start Time</Text>
      <TextInput
        style={styles.input}
        value={startTime}
        onChangeText={setStartTime}
        placeholder="09:00"
      />
 
      <Text style={styles.label}>End Time</Text>
      <TextInput
        style={styles.input}
        value={endTime}
        onChangeText={setEndTime}
        placeholder="10:00"
      />
 
      <Text style={styles.label}>Notes</Text>
      <TextInput
        style={[styles.input, styles.memoInput]}
        value={memo}
        onChangeText={setMemo}
        placeholder="Additional details (optional)"
        multiline
        numberOfLines={4}
      />
 
      <View style={styles.buttonRow}>
        <TouchableOpacity style={styles.cancelButton} onPress={onCancel}>
          <Text style={styles.cancelText}>Cancel</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.saveButton} onPress={handleSave}>
          <Text style={styles.saveText}>Save</Text>
        </TouchableOpacity>
      </View>
    </ScrollView>
  );
}
 
// Expected output:
// A clean form with fields for title, times, and notes.
// Tapping "Save" validates the input and saves the event.
// If the title is empty, an alert reads "Please enter a title for this event."
 
const styles = StyleSheet.create({
  container: { padding: 20 },
  label: { fontSize: 14, fontWeight: '600', color: '#555', marginTop: 16, marginBottom: 6 },
  input: {
    borderWidth: 1, borderColor: '#DDD', borderRadius: 8,
    padding: 12, fontSize: 16, backgroundColor: '#FAFAFA',
  },
  memoInput: { height: 100, textAlignVertical: 'top' },
  buttonRow: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 24 },
  cancelButton: {
    flex: 1, marginRight: 8, padding: 14, borderRadius: 8,
    borderWidth: 1, borderColor: '#DDD', alignItems: 'center',
  },
  cancelText: { fontSize: 16, color: '#666' },
  saveButton: {
    flex: 1, marginLeft: 8, padding: 14, borderRadius: 8,
    backgroundColor: '#4A90D9', alignItems: 'center',
  },
  saveText: { fontSize: 16, color: '#FFF', fontWeight: '600' },
});

Step 4: Persist Data with AsyncStorage

To make sure events survive app restarts, we'll use AsyncStorage for local persistence.

// storage.ts — Data persistence utilities
import AsyncStorage from '@react-native-async-storage/async-storage';
 
const EVENTS_KEY = '@calendar_events';
 
export type CalendarEvent = {
  id: string;
  title: string;
  date: string;
  startTime: string;
  endTime: string;
  memo: string;
};
 
// Load all events
export async function loadEvents(): Promise<CalendarEvent[]> {
  try {
    const json = await AsyncStorage.getItem(EVENTS_KEY);
    return json ? JSON.parse(json) : [];
  } catch (error) {
    console.error('Failed to load events:', error);
    return [];
  }
}
 
// Save all events
export async function saveEvents(events: CalendarEvent[]): Promise<void> {
  try {
    await AsyncStorage.setItem(EVENTS_KEY, JSON.stringify(events));
  } catch (error) {
    console.error('Failed to save events:', error);
  }
}
 
// Add a single event
export async function addEvent(
  event: Omit<CalendarEvent, 'id'>
): Promise<CalendarEvent> {
  const events = await loadEvents();
  const newEvent: CalendarEvent = {
    ...event,
    id: Date.now().toString(),
  };
  events.push(newEvent);
  await saveEvents(events);
  return newEvent;
}
 
// Delete an event
export async function deleteEvent(id: string): Promise<void> {
  const events = await loadEvents();
  const filtered = events.filter((e) => e.id !== id);
  await saveEvents(filtered);
}
 
// Expected output:
// loadEvents() → Returns the saved events array (empty array on first call)
// addEvent({...}) → Assigns a new ID, saves, and returns the created event
// deleteEvent('123') → Removes the event with that ID and saves the updated list

This pattern gives you a solid local storage foundation. When you're ready to scale up, you can swap AsyncStorage for Supabase or Firebase to enable cloud syncing across devices.


Step 5: Add Reminder Notifications

Let your users receive a reminder before their events by adding local push notifications. Tell Rork:

Add a feature that sends a local notification reminder 15 minutes before each event.
Use Expo Notifications and schedule it automatically when an event is saved.

For a deeper dive into push notifications, check out "Implementing Push Notifications in Rork."


Common Errors and Fixes

Calendar Doesn't Render

This usually means react-native-calendars wasn't installed properly. Run npx expo install react-native-calendars in the Rork terminal to fix it.

Events Disappear After Restarting the App

Double-check that AsyncStorage is correctly imported from @react-native-async-storage/async-storage. Verify the package is listed in your package.json.

DateTimePicker Looks Different on iOS vs. Android

This is expected — @react-native-community/datetimepicker uses each platform's native picker UI. If you want a consistent look across platforms, ask Rork to build a custom modal picker instead.


Taking It Further

Once you have the basics working, consider adding these features to make your app stand out:

Week and day views: Beyond the month view, adding a weekly timeline or daily hour-by-hour view makes the app feel much more professional and practical for power users.

Category tagging with color coding: Let users tag events as "Work," "Personal," "Health," etc., with distinct colors. This makes the calendar far more scannable at a glance.

Recurring events: Automatically generating weekly or monthly recurring events is a huge quality-of-life feature that users love.

Google Calendar sync: Using Expo's expo-calendar library, you can integrate with the device's native calendar for seamless two-way sync.


Wrapping Up

In this tutorial, we walked through building a calendar and schedule management app with Rork — from setting up the calendar UI and event forms, to persisting data with AsyncStorage, and adding reminder notifications.

Building a calendar app is an excellent way to practice core mobile development skills: component design, state management, and data persistence. Use what you've learned here as a foundation, and customize it into the perfect scheduling tool for your target audience.

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 $15 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-05-05
Native App or PWA? Three Questions to Answer Before Building with Rork
Should you build a native app with Rork or go with a PWA? This guide breaks down the real functional differences — push notifications, camera, App Store distribution — and gives you a clear decision framework.
Getting Started2026-03-28
UX Design Patterns for Rork Apps — Screen Layouts, Micro-Interactions, and Practical Techniques to Dramatically Improve User Experience
A practical guide to dramatically improving the UX of apps built with Rork. Learn screen layout patterns, navigation design, micro-interactions, onboarding flows, and accessibility best practices to transform your AI-generated app into a professional-grade product.
Getting Started2026-03-23
How to Build a Pomodoro Timer App with Rork — A Complete Beginner's Tutorial
Learn how to build a fully functional Pomodoro timer app with Rork, step by step. Covers timer logic, session management, circular progress bars, and haptic notifications — all in under 30 minutes.
📚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 →