Cross-Platform Mobile · React Native
Production-grade cross-platform social application for iOS, Android, and web — built from inception with a single shared codebase, real-time messaging, and a 50+ component library.
Production-ready cross-platform app — iOS, Android, and web from a single codebase
Built a production-ready cross-platform social application for pet owners from the ground up. The platform lets pet parents share moments, connect with other families, discover local events, and build community — with a consistent experience across iOS, Android, and web from a single React Native codebase.
Joined at day zero, contributing to critical architecture decisions and establishing the patterns that made rapid, scalable development possible. Built 50+ reusable components, implemented real-time features with GraphQL subscriptions, and resolved 40+ cross-platform compatibility issues to ship a polished, production-quality app.
PetoLab needed a comprehensive social platform built from scratch — capable of handling real-time social interactions, media-heavy content, and consistent UX across three platforms simultaneously. The architecture had to support future scaling to thousands of concurrent users without performance degradation.
Identical UX and visual consistency across iOS, Android, and web — with platform-specific optimisations where they matter.
GraphQL subscriptions for instant messaging, live notifications, and dynamic feed updates — without polling.
Image and video uploads, compression, thumbnail generation, and adaptive playback across platforms with varying hardware.
English and Chinese language support with proper pluralisation, locale-aware formatting, and instant runtime switching.
React Native + Expo 52
50+ reusable components, platform adapters (.native.tsx / .web.tsx), responsive layouts
Redux Toolkit
Feature slices, async thunks, memoised selectors, normalised relational state
GraphQL + REST (Apollo Client)
Queries, mutations, WebSocket subscriptions, media uploads
Expo Modules + Firebase
Camera, location, push notifications (FCM), Firebase Auth, Google/Apple sign-in
iOS · Android · Web
Platform-specific optimisations isolated behind file-extension adapters
// Cross-platform request flow
Presentation (React Native / Web)
→ State (Redux Toolkit + async thunks)
→ API Layer (Apollo Client — queries / mutations / subscriptions)
→ Platform Services (Expo Modules / Firebase)
→ Native Platforms (iOS · Android · Web)Comprehensive Component Library — 50+ Components
Cross-platform UI foundation that accelerated every feature built after it
Challenge: Build reusable components that behave identically on iOS, Android, and web while taking advantage of native performance where it matters — without duplicating logic.
Solution: Architected a library with platform file-extension adapters, a theming system, and responsive layouts from day one. Every component documented in Storybook.
Component categories// Image.web.tsx — web renderer
export const Image = ({ source, style, ...props }) =>
<img src={source.uri} style={style} {...props} />;
// Image.native.tsx — iOS + Android (uses FastImage for caching)
import FastImage from '@d11/react-native-fast-image';
export const Image = ({ source, style, ...props }) =>
<FastImage source={source} style={style} {...props} />;
// Shared usage — same import on every platform
import { Image } from '@/components/Image';Real-Time Social Features via GraphQL Subscriptions
Instant messaging, live notifications, and feed updates over WebSocket — zero polling
Challenge: Build a social platform where messages arrive instantly, notifications fire live, and feeds update without polling — matching what users expect from modern social apps.
Solution: Integrated Apollo Client's GraphQL subscriptions over WebSocket, with optimistic UI updates so interactions feel instant before server confirmation.
export const useChatSubscription = (chatId: string) => {
return useSubscription(NEW_MESSAGE_SUBSCRIPTION, {
variables: { chatId },
onData: ({ client, data }) => {
// Write directly into Apollo cache — no refetch needed
client.cache.modify({
id: client.cache.identify({ __typename: 'Chat', id: chatId }),
fields: {
messages: (existing) => [data.data.messageAdded, ...existing],
},
});
},
});
};Redux Architecture for Complex State Management
Feature slices, async thunks, normalised state, and optimistic updates
Challenge: Managing user sessions, content feeds, chat history, real-time updates, and UI state across 20+ screens — without performance degradation or race conditions.
Solution: Feature-based Redux Toolkit architecture with normalised state, memoised selectors, and async thunks keeping all business logic out of components.
authSlice — sessions and tokenspostSlice — feed and post managementchatSlice — messaging and conversationspetProfileSlice — pets and relationshipseventSlice — events and attendancenotificationSlice — activities and alertsconst postSlice = createSlice({
name: 'post',
initialState: { feed: [], loading: false, hasMore: true },
reducers: {
// Flip like instantly — server confirms (or reverts) asynchronously
optimisticToggleLike: (state, { payload: postId }) => {
const post = state.feed.find(p => p.id === postId);
if (post) post.isLiked = !post.isLiked;
},
},
extraReducers: builder => {
builder
.addCase(fetchPostFeed.pending, s => { s.loading = true; })
.addCase(fetchPostFeed.fulfilled, (s, { payload }) => {
s.feed = [...s.feed, ...payload.posts];
s.hasMore = payload.hasMore;
s.loading = false;
});
},
});Multilingual Support with LinguiJS
English + Chinese, 1,000+ strings, zero runtime parsing overhead
Challenge: Support English and Mandarin Chinese with proper pluralisation, locale-aware formatting, and instant runtime language switching — without a performance hit from runtime string parsing.
Solution: LinguiJS 5 with automated message extraction, compiled PO catalogs (no parsing at runtime), and TypeScript integration for compile-time validation of missing keys.
Automated extraction from source, PO file management, compiled catalogs — zero parsing overhead at runtime.
Pluralisation rules, number/date/currency formatting, time zone awareness, RTL layout preparation.
TypeScript autocomplete, compile-time validation, hot reload support, Storybook locale switcher.
import { Trans, t } from '@lingui/macro';
import { useLingui } from '@lingui/react';
export const NotificationBanner = ({ count }: { count: number }) => {
const { i18n } = useLingui();
return (
<View>
<Text><Trans>Welcome to PetoKingdom!</Trans></Text>
<Text>
{i18n._(t`You have ${count} new ${count === 1 ? 'notification' : 'notifications'}`)}
</Text>
</View>
);
};
// zh.po catalog
// msgid "Welcome to PetoKingdom!"
// msgstr "欢迎来到宠物王国!"Cross-Platform Compatibility — 40+ Issues Resolved
Systematic debugging across iOS, Android, and web to ship production-quality UX
Challenge: Cross-platform development surfaces subtle but user-facing bugs — styling inconsistencies, keyboard handling, native module incompatibilities — that only appear on specific platforms.
Solution: Established a platform-testing discipline running every feature on a physical iOS device, Android emulator, and web before closing a ticket. Issues tracked and categorised by root cause.
Advanced Media Handling & Optimisation
Image and video pipelines from capture to CDN — 60–80% video size reduction
Challenge: A social app lives and dies by media quality and speed. Image and video uploads needed compression, thumbnail generation, HEIC conversion, and cross-platform playback without blocking the UI.
Solution: Built separate image and video pipelines using Expo Image Picker, Vision Camera 4, and FFmpegKit — with background upload, progress tracking, and adaptive quality by connection speed.
Capture, gallery pick, crop, HEIC→JPEG, 70% quality compression, thumbnail generation, chunked CDN upload.
Recording with time limits, trim editor, FFmpeg H.264 compression, poster frame extraction, background upload.
FastImage LRU cache, progressive JPEG, WebP on Android, lazy loading in virtualised lists, CDN integration.
import { FFmpegKit, ReturnCode } from 'ffmpeg-kit-react-native';
const compressVideo = async (inputPath: string): Promise<string> => {
const output = `${FileSystem.cacheDirectory}v_${Date.now()}.mp4`;
// H.264 · CRF 28 · fast preset · AAC audio · web-optimised moov atom
const cmd = `-i ${inputPath} -c:v libx264 -crf 28 -preset fast ` +
`-c:a aac -b:a 128k -movflags +faststart ${output}`;
const session = await FFmpegKit.execute(cmd);
if (ReturnCode.isSuccess(await session.getReturnCode())) return output;
throw new Error(await session.getOutput());
// Typical result: 60–80% smaller file, imperceptible quality loss
};Core framework
State & data
UI & animation
Navigation
Media
Platform services
Internationalisation
Testing & tooling
Building from inception taught that choosing cross-platform development and a modular component architecture at day zero either accelerates or constrains every week that follows. Upfront investment pays multiplicatively.
Virtualisation, lazy loading, and image caching are orders of magnitude harder to retrofit. Building them in from the start meant the app stayed smooth even as the feature set grew to 20+ screens.
Every hour invested in the component library in month one saved two hours in every month after. The 40% acceleration wasn't a one-time gain — it compounded with every feature shipped.
Cross-platform development looks identical in code and catastrophic in practice. The 40+ resolved bugs were almost all invisible on the development platform and immediately visible on the other two.
Redux Toolkit's slice pattern and async thunks kept business logic outside components. Without normalised state, the relational data (users, pets, posts, chats) would have become a maintenance burden at scale.
GraphQL subscriptions with optimistic updates created the responsiveness users expect — but reliability came from treating reconnection, error states, and subscription cleanup as first-class concerns, not edge cases.