In this case study

Product

The mobile app is the primary client. The web design brief requires the web client to mirror the app's queries rather than add features of its own. The table shows where each feature lives.

Feature map

FeatureMobileWebBackend modulesNotes
Feed (13 modes, ranked for-you)yesyesfeed, feedRanked, ranking, feedHealthWeb mirrors ranked and home feeds with seen and dwell tracking
Posts: 20 images, 600 s video, polls, clips, draftsyesyesposts, postCounters, drafts, musicClipsSeparate 2.9k and 3.2k-line composers per platform on mobile
Comments, replies, voice comments (60 s), stickersyesyescomments, commentStickersiOS shows comments as a form sheet; Android opens full detail
Staðan storiesyesyesdailyUpdatesA player pool hands pre-buffered video to the viewer
Encrypted messaging, group chats, voice, chat eventsyesyesmessaging, encryption, chatEvents5,056-line conversation screen on mobile
Groups (Svæði), dues roster, invites, join requestsyesyesgroups, groupPosts, groupDues, schoolEmailsDues are recorded only; Slapp takes no money
Markaðstorg marketplaceyesyes14 markad modulesTrades, double-blind reviews, saved-search alerts, photo provenance
Leigumarkaður rentalsyesyes16 leiga modules6 UI languages; AI parses listings from text or screenshots
Deit dating (18+)yesnodating, datingUpdateCampaignWeb has a middleware entry for /ast but no page
Events and event postsyesyesevents, eventPostsWeb adds an ICS feed and a generated OG image
Síður pages, automated pagesyesyespages, automatedPages, content modulesAdmin screens for news and daily-question approval live in the mobile app
Enski boltinn predictoryesyespremierLeague modulesLive since 18 August 2026; per-minute fixture sync during match windows
HM leikur World Cup predictoryesnoworldCup (8 modules)Crons retired; data frozen after the tournament
Stafli, geo game, daily challenge, trophiesyespartialstafli, geoGame, dailyChallenge, gameTrophiesWeb has the hashtag challenge only
Djamm party modeyesyesdjamm, party8-hour party status, 1-minute drink cooldown
Þjóðhátíð festival modeyesnothjodhatidHard-expired 3 August 2026
Tilboð offers, clubs, redemptionyesyes21 tilbod modulesPIN, code and tap redemption; merchant side in its own app
Slapp+ purchaseyesnosubscription, loyaltyCustom paywall on mobile; web explains that purchase happens in the app stores
Slapp+ perks (viewers, analytics, private group)yesyesloyalty, slappPlusGroup, postMilestones
Friend map, location sharingyesnomapLocationsBy design, no web map
Tengslakort connection cardsyesnotengsl, graphSuggestions"Only joint facts" between two users
Monthly wrapped, referrals, birthdaysyesyeswrapped, referrals, birthdayThe referral competition is currently disabled server-side
Member tee shopyesnoshopMobile links to the shop app; Slapp+ has no shop perks
Share-to-Slapp, Instagram importyespartialinstagramImport, linkPreviewsiOS share extension; web has /deila

Feed

The feed is the core loop: a friends-plus-discovery list of posts with up to 20 images, video up to 600 seconds, polls, stickers and attached music clips. It has 13 modes, and the production read path is the ranked query described in the feed ranker chapter. Posts can be drafted, and the two post composers on mobile are separate components, 2.9k and 3.2k lines, chosen at runtime per platform.

Comments support replies, 60-second voice comments and stickers. iOS shows comments as a form sheet; Android opens the full post detail. Dwell and view-duration hooks in the feed feed the ranker's training data: how long a post stayed on screen, whether the detail view opened, whether fullscreen opened.

Staðan

Staðan, "the status", is the daily photo or video story, with views, likes and comments, stored in the dailyUpdates table. On mobile a story player pool hands pre-buffered players to the viewer so the next story starts without a stall.

Messaging

Direct and group chats, with voice messages, images, shared posts and in-chat events. Every conversation has its own encryption key, as described in the architecture chapter. Conversations carry denormalised participant IDs and a per-participant unread count, and a kind flag keeps marketplace and rental threads out of the main inbox. Typing status lives in its own table to avoid write conflicts. Sending is rate-limited at 1,000 messages per minute and checks block relationships, and unread counts come from an aggregate. The conversation screen on mobile is the largest file in the app, at 5,056 lines.

Svæði and the two marketplaces

Svæði, "areas", is the groups hub. Groups have posts, invites, join requests and a dues roster; dues are recorded only, and Slapp takes no money. Student associations onboard members through school email lists (Skólanetföng). A per-section allowlist gates early access to new Svæði features.

Two marketplaces live inside it, and both require the user to be 18, which the identity model makes enforceable.

  • Markaðstorg is a consumer-to-consumer marketplace with trades, double-blind reviews, saved-search alerts every 15 minutes, and photo provenance. Listings and wanted ads have their own canonical URL prefixes, Vara and Óska. Fourteen backend modules, including a risk model that adjusts surfacing from weighted signals without hiding anything automatically.
  • Leigumarkaður is a rental market with owner verification, and it is the one part of the app that is not Icelandic-only: six UI languages (Icelandic, English, Polish, Lithuanian, Ukrainian and Spanish) from lazily loaded string tables, persisted so that push bodies follow the choice. AI parses listings from text or screenshots and translates them into the other five languages at write time. dHash photo fingerprints block listings whose photos match an external index. Sixteen backend modules.

Deit

Deit is the 18+ dating tab, with a swipe deck, top picks, question games and its own message stack. It exists only on mobile; the web has a middleware entry for its code path, ast, but no page. The tab is rendered only when the user is eligible, and eligibility is cached in MMKV so the native tab bar is correct on the first frame. Limits: 200 swipes per hour and 20 free dating likes per day. Feeds refill every six hours, top picks are computed at 11:00, and the Elo ratings that order the deck are replayed every Monday.

Events

Events can be standalone, bound to a group, or bound to a chat. They have RSVP, co-hosts, photos, event posts and public calendar feeds. Reminders go out every 15 minutes. The web client adds an iCalendar feed and a generated 1200 by 630 image for each event.

Síður and automated pages

Síður are multi-author pages. Some of them post without a human. Nine backend modules run them:

  • A daily question generated by gpt-5.5 and approved in-app before it posts.
  • RSS ingestion from RÚV, mbl, Vísir, DV, Heimildin and fotbolti.net every 30 minutes, with morning and afternoon digests at 07:50 and 16:50 that check their own summaries for entailment before publishing.
  • A 07:20 weather post and CAP warnings from Veðurstofa, the Icelandic Met Office.
  • An earthquake watch every 15 minutes with a Sunday recap.
  • Wikipedia's most-read article daily, and weekly exchange-rate and fuel-price posts.

The admin screens for approving news and the daily question live in the mobile app, not the admin dashboard.

Games

Stafli is an Icelandic Wordle. There is a daily Street View geo game, a daily hashtag challenge, and a trophy system with a Slapphrifavaldur badge at 1,000 points. Two football predictors sit beside them: HM leikur, the World Cup predictor, whose crons are retired and whose data is frozen after the tournament, and Enski boltinn, the Premier League predictor, live since 18 August 2026 with per-minute fixture syncing during match windows. The two football games are the only things that run outside Convex, as two small prediction APIs. On web, only the hashtag challenge exists.

Seasonal modes

Djamm is party night mode: drink logs and leaderboards, with an 8-hour party status and a 1-minute cooldown between drinks. Þjóðhátíð was the festival mode for the Westman Islands festival; it hard-expired on 3 August 2026.

Tilboð

Tilboð, "offers", is the business marketplace. Merchants buy monthly ad slots, run loyalty clubs and redeem offers at the till by PIN, code or tap. The merchant side is its own app. Twenty-one backend modules. The slot tiers, the offer lifecycle and the loyalty plans are on the monetisation page.

Slapp+

Slapp+ is a RevenueCat subscription. Its perks: profile viewers, post analytics, incognito viewing, a private group, and dating perks. Purchase happens in a custom paywall on mobile; the web client explains that the purchase is made in the app stores and shows the perks it can: viewers, analytics and the private group. How the entitlement flows from RevenueCat into the app is on the monetisation page.

Other features

  • A friend map with location sharing, mobile only; there is deliberately no web map.
  • Tengslakort, connection cards between two users that show only facts both of them share.
  • A monthly wrapped, referrals (the referral competition is currently disabled server-side), and birthdays.
  • The member tee shop, linked from mobile and described with monetisation.
  • Share-to-Slapp through an iOS share extension and a web route, and an Instagram import.

The mobile app

The app is the primary client: Expo Router with 408 route files, five tabs, native iOS tabs, a custom Android tab bar, and a data layer tuned for first-frame paint from device cache.

Runtime
Expo, React Native, React, Hermes, the New Architecture, the React Compiler, typed routes.
Router
Expo Router with NativeTabs on iOS and a custom bar on Android.
Updates
expo-updates with the runtime version bound to the app version. Checks on every launch, downloads on foreground, reloads only on backgrounding, and holds during sign-in.
Builds
EAS profiles for development, preview and production, with auto-increment. Channels match. Submitted as Vestra AI ehf.
Dependencies
74 runtime and 6 dev. MMKV in 38 files and no AsyncStorage. Reanimated, gesture-handler, FlashList, expo-video, expo-image, expo-maps, expo-glass-effect, RevenueCat, noble ciphers.
Native
No custom native modules or config plugins. Two Xcode targets: the app and the share extension. The ios and android folders are prebuild output and untracked.
Fonts and icons
System fonts only. Ionicons in 370 files, 2,502 uses. SF Symbols only in the native tab bar and one icon wrapper.

The tab bar is Heim (home), Svæði (groups), Deit (dating), Prófíll (profile) and Leita (search). On iOS the profile icon is the user's own avatar, rasterised off-screen to a 26-point circular PNG and cached.

Each tab owns a stack, and the same shared screens (post, profile, page, group, hashtag, story viewer) are registered inside every stack. As a result, 196 of the route files are under eight lines: they re-export a screen from a shared folder with a base path. The root layout registers roughly 60 more screens as modals, form sheets and deep-link redirects, guarded on auth state and account deletion.

StackRoute filesScreens registeredLayout size
Home83551,313 lines
Groups88631,106 lines
Profile5138855 lines
Search3621
Dating65
Root, outside the tabs143about 602,525 lines

Deep links use the slapp:// scheme plus universal links on slapp.is for nine path prefixes. A native-intent file rewrites web URL shapes onto app routes, and a pending-route mechanism waits for auth and the user document, navigates to the tab root first, then pushes the target, so the back button always has a screen to return to.

Data layer

  • Convex hooks: 473 useQuery, 452 useMutation, 62 usePaginatedQuery, 31 useAction.
  • Polled hooks: three custom hooks, plain, paginated and delta, used 120 times. They call a query once, refetch on demand, time out at 20 seconds, retry with a 2 to 30 second backoff, refetch on foreground, and drop stale responses through a generation counter. The reasoning is in the architecture chapter.
  • Optimistic updates on 33 mutations across feed, profile, groups, events, pages and messaging.
  • Snapshots: 20 MMKV stores implement stale-while-revalidate first paint for the feed, stories, groups, explore, profile, conversations, engagement, eligibility, the tab avatar, video URLs, prices, and the two sports games.
  • Auth bridge: a custom hook replaces the stock Convex Better Auth provider. It caches and dedupes token fetches, resolves signed-out only on a definitive 401 or 403, uses hysteresis before flipping state, hydrates from SecureStore on cold start, and can recreate the Convex client to recover a zombie WebSocket after a long background.
  • Sign-in: Kenni through the OAuth flow. On Android the session is redirected to the system browser with a five-minute timeout because Chrome Custom Tabs drop SIM-based authentication, and a progress marker in MMKV lets the app resume if an over-the-air reload interrupts the flow.

Media pipeline

  • Images: compression presets (feed 1,200 px at quality 0.85, avatar 400 px, full 1,600 px, comment 600 px), an optional blurhash, then a presigned R2 PUT with a metadata sync and a size check before the key is trusted.
  • Video: the thumbnail goes to R2, the video to Cloudflare Stream through tus in 10 MiB byte-range chunks, with a 600-second cap and 20 uploads per day.
  • Playback: expo-video with a shared buffer config, a warm pool of up to three detached players, the story player pool, and prefetching of upcoming feed videos. The immersive viewer rotates through a transform so the app stays portrait-locked and the change ships over the air.
  • expo-image in 277 files, a memory-disk cache policy in 192 places, recycling keys in 55 files, and a memory-pressure hook that clears the cache on warnings.
  • An image editor built as an expo-gl shader with brightness, contrast, saturation, temperature, shadows, highlights, vignette and fade uniforms, plus filter presets.

Design system

One accent, orange, on white or near-black. #f97316accent #101112dark background Text is #11181Clight and #ECEDEEdark; borders are #e1e8edlight and #252729dark. Features carry their own palettes beside their code: dating pink and purple, Slapp+ gold #c9a84c, World Cup gold, Premier League. Liquid Glass availability is checked in 34 files to switch header transparency and paddings, but a glass view is rendered in exactly one screen. Haptics are used in 93 files. A measured audit of the app's type, radii and hues proposes a three-role type scale and a single orange.

Push notifications

Registration is deferred past first paint and runs once per session. Foreground pushes are suppressed at the OS level and drawn by an in-app banner instead. Tap handling routes by payload type (message, comment, like, reply, group post, profile views) and rewrites targets that point into another tab to a root-level equivalent, so a push never opens a screen without a back stack. There are no notification categories or action buttons. Which pushes are sent at all is decided by the notification ranker.

Performance habits

  • FlashList in 12 list-heavy screens; FlatList remains the default in 134 files.
  • The React Compiler plus 1,201 useCallback, 347 useMemo and 99 memo wrappers.
  • Metro inline requires, root stack options hoisted to constants so the native header never re-lays out, and a deferred-after-paint helper that replaces InteractionManager in 12 files.

Localisation and platform split

The UI is hardcoded Icelandic with no i18n library. The exception is Leigumarkaður, described above. Platform.OS appears 654 times in 198 files; there is one platform-specific file for iOS and none for Android. The two post composers are separate components chosen at runtime.

The other surfaces

The web client is a full signed-in client and the landing page for every URL the app shares, rebuilt in September 2026 into a three-column shell: a left nav of nine items, a 600 px centre column, a right column with search, trending, suggestions and events, a messages dock and a global compose dialog. Phone width gets an install banner and a bottom tab bar. It has 93 pages: 6 public, 9 canonical deep-link pages, and 78 in-app client pages. Posts, profiles, groups, events, marketplace items, wanted ads, rentals, club invites and share-to-Slapp each have a server-rendered page with generated metadata; signed-in visitors see the full screen and logged-out visitors see a teaser with a sign-in bar and one attempt at the app scheme. It ships HSTS with preload, a full content security policy, frame denial in production, and 11 permanent redirects from legacy in-app paths. Not on web: dating, the World Cup game, Stafli, the geo game, connection cards, the friend map and the Slapp+ purchase.

The admin dashboard is 42 pages behind the admin flag, dark-only with its own accent, split into overview, users, analytics and business tooling. It has a user detail page, the reports queue, moderation and listing queues, 18 analytics pages including every model's weights and training history, and the Tilboð business tooling. What it cannot do: ban (bans are set through backend functions; the page only shows the badge), trigger migrations, or edit feature flags beyond the allowlist.

The Tilboð portal and the shop are described with monetisation.

Glossary

The codebase, the interface and the database use Icelandic names. These are the ones you meet in identifiers and route paths.

TermMeaning
Heimhome tab and feed
Svæði"areas": the groups hub, also hosting the marketplaces
Deitthe dating tab (code path ast, "love")
Prófíllprofile tab
Leitasearch tab
Staðan"the status": daily stories, table dailyUpdates
Póstacompose a post
Síðurpages: multi-author publications
Tilboðoffers: the business ad and loyalty marketplace
Vildarklúbbura merchant's loyalty club
Bókhaldaccounting: the merchant invoice page
Leiga, Leigumarkaðurrental market
Markaðstorgconsumer marketplace
Vara, Óskaa listing, a wanted ad (canonical URL prefixes)
Klúbbboðclub invitation (route /klubbbod)
Deilashare (the share-to-Slapp route)
KennitalaIcelandic national ID number; encrypted at rest
Kennithe eID provider used for sign-in
Staflithe Icelandic Wordle
Djammparty night mode
Þjóðhátíðthe Westman Islands festival mode
Enski boltinnPremier League predictor
HM leikurWorld Cup predictor
Tengsl, Tengslakortconnections, the friend-graph card
Slapphrifavaldur"Slapp influencer" badge at 1,000 points
Félagsgjald, árgjaldmembership dues recorded for groups
Spurning dagsinsquestion of the day page
Fréttir, Veðrið, Skjálfta, Fróðleikurautomated news, weather, earthquake and trivia pages
Skólanetföngschool email onboarding for student associations
Hver er ég"who am I": the direction and school-verification flow
Slapp arithe mascot on the tee