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-21Intermediate

Building a Settings Screen That Actually Works in Your Rork App — Notifications, Subscription Management, and Legal Requirements

A practical guide to building production-grade settings screens for your Rork app, covering notifications, subscription management, legal disclosures, and support flows — with real code examples.

Rork515Settings ScreenUX5App Development33React Native209Expo149Subscription23

When I shipped my first app, I cut corners on the settings screen. "Notification toggle and a link to the terms of service should be enough," I thought. Three days after launch, the top three support questions I received were all about that screen: "How do I silence notifications during specific hours?", "How do I cancel my subscription?", and "How do I delete my account?" Every one of those could have been self-served with a decent settings screen.

Settings screens are rarely glamorous, but they shape how your app feels to use and whether people stick around. This guide walks through a structure I rely on, and concrete implementation patterns for notifications, subscription management, and the legal plumbing you cannot skip.

Organize Settings Into Three Layers: Legal, Usability, Conversion

If you leave the settings screen to grow organically, it ends up as a bag of toggles nobody can mentally map. In apps I maintain, the cleanest structure I have found groups items into three buckets.

  • Legal (required): terms of service, privacy policy, open-source license credits, and — for Japan-specific releases of paid apps — the Tokushoho disclosure
  • Usability (experience): notifications on/off, dark mode, language, sign out, account deletion
  • Conversion (revenue / feedback): upgrade to paid, rate the app, share with friends, contact support

I typically lay them out top-to-bottom as: App Preferences → Account → Info (with legal) → Feedback. It is tempting to put the "upgrade" button at the very top, but users open settings to change something practical more often than to pay, and honoring that is how you build trust.

Prompting Rork to Generate the Settings Screen

Rork gives you a lot of freedom, which means the quality of the output depends heavily on how specific your prompt is. Here is the skeleton I reuse when asking Rork to build a settings screen.

Create a SettingsScreen with the following requirements:

- Placed at /settings on Expo Router
- Four sections:
  1. App Preferences: notifications (Switch), dark mode (Switch), language picker
  2. Account: sign out, delete account (with confirmation dialog)
  3. Info: terms, privacy policy, version, open-source licenses
  4. Feedback: rate the app, contact support
- Visual style: grouped list in the style of iOS Settings
- State lives in the existing useSettingsStore (Zustand) — do not introduce another state library
- accessibilityRole and accessibilityLabel on every interactive element

The critical line is "do not introduce another state library." Without it, Rork will happily pull in Redux, Jotai, or Recoil depending on what it has seen recently, and you end up rewriting half the app. Naming your existing store, colors, and typography in the prompt saves hours of rework.

Notifications: Always Sync the OS Permission With the App Toggle

The most common bug in a settings screen is a desynced notification toggle. Many developers treat the switch as a simple boolean, forgetting that iOS and Android manage notification permission at the OS level independently. The result: the switch is on, but no notifications arrive, and the support tickets start flowing.

// components/NotificationToggle.tsx
// Keeps the app-side toggle in sync with OS permission state.
import * as Notifications from "expo-notifications";
import { Switch, Alert, Linking } from "react-native";
import { useEffect, useState } from "react";
 
export function NotificationToggle() {
  const [enabled, setEnabled] = useState(false);
 
  // On mount, read the OS permission and reflect it in the UI.
  useEffect(() => {
    Notifications.getPermissionsAsync().then((status) => {
      setEnabled(status.granted);
    });
  }, []);
 
  const handleToggle = async (value: boolean) => {
    if (value) {
      const result = await Notifications.requestPermissionsAsync();
      if (result.granted) {
        setEnabled(true);
        return;
      }
      if (\!result.canAskAgain) {
        // The user previously denied permission and we can no longer
        // show the system dialog. Send them to the OS settings.
        Alert.alert(
          "Notifications are disabled",
          "Enable notifications from your system settings.",
          [
            { text: "Cancel", style: "cancel" },
            { text: "Open Settings", onPress: () => Linking.openSettings() },
          ]
        );
      }
    } else {
      // Turn off inside the app without revoking the OS permission.
      setEnabled(false);
      await Notifications.cancelAllScheduledNotificationsAsync();
    }
  };
 
  return (
    <Switch
      value={enabled}
      onValueChange={handleToggle}
      accessibilityLabel="Toggle notifications"
    />
  );
}

The canAskAgain branch is what most people forget. Once a user has denied permission, you cannot prompt again from the app — the OS silently ignores requestPermissionsAsync. If your UI does not route them to system settings in that case, flipping the switch appears to do nothing, and this becomes your #1 support complaint.

For quiet-hours behavior, add a TimePicker below the switch and filter scheduled notifications on the client, or use Android notification channels via setNotificationChannelAsync.

Subscription Management: Do Not Hide the Cancel Path

Both App Store Review Guideline 3.1.2 and Google Play Policy 10.6 effectively require that users can reach the subscription management page from inside your app. You are not required to process cancellation yourself — you just need to make the platform's own page one tap away.

// components/SubscriptionManageButton.tsx
// Opens the platform's subscription management page.
import { Linking, Platform, Pressable, Text } from "react-native";
 
export function SubscriptionManageButton() {
  const openManagePage = () => {
    if (Platform.OS === "ios") {
      Linking.openURL("https://apps.apple.com/account/subscriptions");
    } else {
      Linking.openURL("https://play.google.com/store/account/subscriptions");
    }
  };
 
  return (
    <Pressable
      onPress={openManagePage}
      accessibilityRole="button"
      accessibilityLabel="Manage or cancel subscription"
    >
      <Text>Manage or cancel subscription</Text>
    </Pressable>
  );
}

Burying this button under nested menus to reduce churn is a short-term trick that rebounds as refund requests, one-star reviews, and rejected updates. I always place it in the Account section where it is easy to find on first glance.

For the subscription flow itself, the Rork + Stripe subscription complete guide covers the server-side and Checkout pieces.

Driving Conversion Without Being Pushy

The settings screen is a touchpoint with people who are already using your app, so conversion rates here tend to be higher than on your landing page. In my apps, roughly 30% of paid signups originate from the settings screen.

Two patterns work well when placed at the end of a section or in a dedicated card, not at the top:

  • Upgrade card for free users — show a clean "Upgrade to Pro" card for free-tier users, and hide it entirely for paying users. Continuing to show the upgrade prompt to subscribers is the fastest way to trigger a cancellation
  • Review prompt — on iOS use expo-store-review's requestReview(), and on Android use the Google Play In-App Review API. Note that both platforms quietly rate-limit these calls, so gate them behind something like "app launched at least 10 times" and "no review prompt shown in the last 30 days"

For timing the review prompt well, see the in-app review prompt design guide for iOS and Android.

Account Deletion: An Apple Requirement You Cannot Ignore

Since iOS apps that offer account creation have been required to offer in-app account deletion (App Store Review Guideline 5.1.1), this has shifted from "nice to have" to a hard gate for approval. Many Rork users I have talked to do not realize this until rejection, and end up scrambling to ship it in a week.

// components/DeleteAccountRow.tsx
// Triggers the account deletion flow with a safety confirmation.
import { Alert, Pressable, Text } from "react-native";
import { useRouter } from "expo-router";
import { deleteAccount } from "../services/account";
 
export function DeleteAccountRow() {
  const router = useRouter();
 
  const confirmDelete = () => {
    Alert.alert(
      "Delete account?",
      "This will permanently remove your account and all associated data. This cannot be undone.",
      [
        { text: "Cancel", style: "cancel" },
        {
          text: "Delete",
          style: "destructive",
          onPress: async () => {
            try {
              await deleteAccount();
              router.replace("/goodbye");
            } catch {
              Alert.alert(
                "Something went wrong",
                "Please try again in a moment or contact support."
              );
            }
          },
        },
      ]
    );
  };
 
  return (
    <Pressable
      onPress={confirmDelete}
      accessibilityRole="button"
      accessibilityLabel="Delete account"
    >
      <Text style={{ color: "#d9534f" }}>Delete account</Text>
    </Pressable>
  );
}

A few things are easy to get wrong. Use style: "destructive" on the confirmation action — the red text is not decoration, it is how users recognize a dangerous operation. Handle the server error path gracefully: if deleteAccount fails, the user is left in a confusing state unless you surface the failure. And remember to also revoke any active subscriptions before deletion, otherwise the user will continue to be billed.

A Note on Usage Analytics

One more pattern worth mentioning: the settings screen is an excellent place to gauge how well your app is actually configured for each user. Tracking which toggles people flip — without tracking the underlying data — gives you a sense of which defaults are wrong. If 80% of users turn off notifications within a day, that is a signal your notification strategy is too noisy, not that users dislike notifications in general. If almost nobody ever changes the language from the default, you can save yourself the effort of maintaining three locale files. Treat the settings screen as feedback, not just as controls.

Closing Thought

Settings screens are easy to overlook, but they are often where users decide whether to keep an app on their phone. If you do one thing after reading this, open your own app's settings and count how many taps it takes to find the cancel flow. If the answer is three or more, fixing that is probably the highest-leverage UX improvement you can ship this week.

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-02
Adding Native Share Functionality to Your Rork App — A Complete Share Sheet Guide
Learn how to implement native Share Sheet functionality in your Rork app. From sharing text and URLs to images and deep links, this beginner-friendly guide walks you through real code examples for iOS and Android.
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-10
Adding React Compiler to Expo Let Me Delete 41 Hand-Written memo Calls
I enabled React Compiler on Rork-generated React Native screens and measured the rerender counts with Profiler. Here is how I decided which memo and useCallback calls were safe to delete, how to find the components the compiler bailed out on, and how to catch regressions in CI.
📚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 →