Skip to content

Template: Add a new feature screen

Status: Reference

Task

Add a new feature screen for <purpose> (e.g. "club members admin", "court directory").

Pre-implementation checks

  1. Read docs/architecture/screen-blueprint.md — does this screen already have a blueprint entry? If yes, follow it. If no, you'll need to add one.
  2. Identify the feature package: which packages/features/<name>/ does it belong to? If none fits, escalate to user before creating a new package.
  3. Grep for related primitives:
    bash
    grep -rn "DetailShell\|MainTabShell" packages/features/<feature>/src/   # see existing shells
  4. Confirm queries exist: the data the screen needs should be available via existing query hooks. If not, write those FIRST (separate prior commit, see add-query.md).
  5. Confirm permission gating: does this screen require admin role? Use useClubRole(clubId) for club-scoped screens.

Files in scope

  • New: packages/features/<feature>/src/<screen-name>-screen.tsx
  • Modified: packages/features/<feature>/src/index.ts (add export)
  • New: apps/mobile/app/<route-path>.tsx (mobile route)
  • New: apps/web/app/<route-path>/page.tsx (web route)
  • Modified: packages/app/src/navigation/routes.ts (add routes.<noun> helper if missing)
  • Modified: packages/app/src/config/i18n/{ko,en}/<file>.ts (add new strings)

Files OUT of scope

  • Mutation/query hooks — those go in @twomore/app, separate commits
  • New shared primitives — only if this screen requires a new pattern (escalate first)

Implementation pattern

tsx
import React, { useState } from 'react';
import { ActivityIndicator } from 'react-native';
import {
  appRouter,
  useRouteParams,
  useAuth,
  use<Entity>,             // query hooks
  use<Mutation>,           // mutation hooks
  t,
} from '@twomore/app';
import {
  DetailShell,            // or MainTabShell for tab pages
  Card,
  SectionBlock,
  Button,
  Badge,
  Text,
  XStack,
  YStack,
  EmptyState,
  FeedList,
  SkeletonCard,
} from '@twomore/ui';
import { <Icon> } from '@tamagui/lucide-icons';

export function <Name>Screen(): React.JSX.Element {
  const { <param> } = useRouteParams<{ <param>: string }>();
  const { userId } = useAuth();
  const { data, isLoading } = use<Entity>(<param>);
  const labels = t().<scope>;

  if (isLoading && !data) {
    return (
      <DetailShell title={labels.pageTitle} onBack={() => appRouter.back()}>
        <YStack padding="$4" gap="$3">
          <SkeletonCard />
          <SkeletonCard />
        </YStack>
      </DetailShell>
    );
  }

  return (
    <DetailShell title={labels.pageTitle} onBack={() => appRouter.back()}>
      {/* SectionBlock for titled groups, FeedList for lists */}
    </DetailShell>
  );
}

Hard constraints to follow (from AGENTS.md)

  • ARCH-2: import only from @twomore/app and @twomore/ui. Never expo-router, next/*, @/, or sibling features.
  • COMP-3: page CTAs in BottomCtaBand; cards never contain <Button> JSX subtrees
  • COMP-4: MainTabShell for tab pages, DetailShell for detail screens
  • COMP-5: FeedList / GroupedFeedList for growable lists, never bare FlatList / SectionList / ScrollView
  • DATA-7: all UI strings via t(), code/comments in English, Korean only in i18n files
  • Reviewer checklist: CHK-COMP-1 (skeletons during loading) — see docs/best-practices/review-checklist.md

Post-implementation verification

bash
git status                                                # confirm changes persisted

yarn typecheck                                            # exit 0

# Workspace-specific
yarn workspace @twomore/<feature> typecheck
yarn workspace @twomore/app typecheck

# Architecture contracts
yarn workspace @twomore/app jest --runInBand src/config/__tests__/architecture-archunit.test.ts

# Build smoke tests
yarn workspace @twomore/web build                         # web compiles
yarn workspace @twomore/mobile preflight                  # mobile preflight passes

What to report

  • Files created/modified
  • Permission gating decision
  • Skeleton/empty state handling
  • Typecheck + arch test results
  • Web build + mobile preflight results
  • Whether screen-blueprint.md was updated (required for new screens)

Markdown remains the source of truth. Run yarn docs:check before handoff.