Skip to content

Stock Photos for Goals & Communities — Feature PRD

Product: Objectuve (codename: Enkidu) Feature: Curated stock photo library for goal and community cover images Status: Shipped — April 2026 Date: April 2026


1. Problem Statement

Goal cover images make the app feel personal and visually engaging — the new prominent image band on GoalGridCard proves this. But the current flow requires users to upload their own photos, which creates friction in two places:

  1. Goal creation. Users creating a goal about "Exercise 4x a week" or "Save for a house" rarely have a relevant photo ready on their phone. The "Add Cover Image" button stays untapped, and the goal gets a gradient placeholder instead.

  2. Edit Goal. Even users who want a cover image face a multi-step process: tap the image area → choose camera or gallery → take/select a photo → crop → save. Most skip it.

The result: many goals have no cover image, which makes the Goals page and Dashboard less visually compelling than they could be.

User feedback confirms this: "It might be nice to have some stock photos they can choose from as well. Not of real people necessarily though."


2. Goals & Success Metrics

Primary Goals

  1. Increase the percentage of goals with cover images
  2. Reduce friction in goal creation by auto-suggesting a relevant image
  3. Maintain the personal, intentional feel — stock photos should enhance, not genericize

Success Metrics

MetricTargetMeasurement
% of goals with cover images60%+ (from ~30% today)Goals with non-null imageUrl / total goals
Auto-suggested image acceptance rate50%+ keep the suggested imageGoals where stock imageUrl persists after 7 days
Stock photo picker usage in Edit Goal20%+ of edit sessionsEvent tracking on picker open
Time to create first goalNo increaseActivation funnel timing
% of communities with cover images70%+Communities with non-null coverImage / total

Guardrails

  • No real people in stock photos (per user feedback). Scenes, objects, landscapes, abstract — not faces.
  • Stock photos must not slow down goal creation. Auto-suggest is instant (local lookup, no API call).
  • Users can always remove the stock photo or replace with their own. Never locked in.
  • No external API dependency at launch. Self-hosted curated set only.

3. User Stories

  • US-1: As a user creating a goal, I want a relevant cover image auto-suggested based on my category, so my goal looks good without extra effort.
  • US-2: As a user editing a goal, I want to browse a grid of stock photos filtered by category, so I can pick one that fits without leaving the app.
  • US-3: As a user, I want to remove a stock photo and upload my own instead, so I keep full control over how my goal looks.
  • US-4: As a user, I want stock photos to feel high-quality and relevant, not generic clip-art.
  • US-5: As a community creator, I want a cover image auto-suggested based on my community's topic, so the community page looks inviting from day one.
  • US-6: As a community admin, I want to browse and pick a stock cover photo when editing the community, so I don't need to source my own image.

4. Feature Design

4a. Stock Photo Library

Structure: A static TypeScript config file mapping category slugs to arrays of photo URLs. No database model, no API endpoint — pure frontend config.

File: ionic_frontend/src/config/stockPhotos.ts

typescript
export interface StockPhoto {
  id: string
  url: string
  alt: string       // Accessibility description
  credit?: string   // Photographer credit (if applicable)
}

export const stockPhotos: Record<string, StockPhoto[]> = {
  'health': [
    { id: 'health-1', url: '/images/stock/health-1.webp', alt: 'Running shoes on a trail' },
    { id: 'health-2', url: '/images/stock/health-2.webp', alt: 'Yoga mat and water bottle' },
    // ... 5-8 per category
  ],
  'career-development': [...],
  'personal-development': [...],
  'wealth-creation': [...],
  'family': [...],
  'relationship': [...],
  'spirituality': [...],
  'life-vision': [...],
  'socializing': [...],
  'other': [...],
}

Photo hosting: WebP format, stored in ionic_frontend/public/images/stock/ (bundled with the app) or uploaded to GCS and referenced by URL. WebP for size efficiency.

Photo curation guidelines:

  • ~5-8 photos per category, ~50-80 total
  • Landscape orientation (16:9 or similar) to fit the card image band
  • No identifiable people (per feedback). Scenes, objects, environments, abstract patterns.
  • Warm, aspirational tone. Not sterile corporate stock.
  • Mix of photography styles: some minimal/clean, some vibrant/energetic
  • Minimum resolution: 1200x800px (matches the max crop size)

Category-to-photo mapping examples:

CategoryPhoto themes
HealthTrail running shoes, yoga mat, fresh produce, sunrise hike, swimming pool, gym equipment (no people)
Career DevelopmentLaptop on desk, notebook and coffee, whiteboard with notes, conference room, bookshelf
Personal DevelopmentJournal and pen, meditation cushion, stack of books, sunrise, plant growing
Wealth CreationPiggy bank, coins stacking, home exterior, investment chart, calculator
FamilyDinner table set, family photo frames, playground, kitchen counter, garden
RelationshipTwo coffee cups, park bench, sunset beach, hiking trail, dinner candles
SpiritualityCandle and incense, mountain vista, water reflection, forest path, temple exterior
Life VisionRoad stretching to horizon, map and compass, mountain peak, bridge at dawn
SocializingCafé exterior, board game, picnic setup, concert venue, park gathering space
OtherColorful abstract, gradient landscape, desk workspace, notepad with pen

4b. Auto-Suggest on Goal Creation

When a user selects a category in GoalForm during creation (not edit), auto-assign a stock photo from that category:

  1. User selects category (e.g., "Health")
  2. stockPhotos['health'] is looked up
  3. A random photo from the set is chosen (seeded by goal name hash for consistency)
  4. The image area shows the stock photo with a subtle "Stock photo" badge
  5. The photo URL is stored as imageUrl in the form state (not as a file upload)

Key behaviors:

  • Only triggers on new goal creation, not when editing an existing goal
  • If user already uploaded their own photo, stock suggestion doesn't override it
  • If user changes category, the stock photo updates to match the new category
  • User can tap the image to either upload their own (replacing the stock photo) or browse more stock options

4c. Stock Photo Picker in Edit Goal

Add a "Browse Photos" option alongside the existing camera upload:

Trigger: When user taps the cover image area in GoalForm (either the placeholder or existing image), show a bottom sheet / action menu with three options:

  1. Take Photo (camera)
  2. Choose from Library (device gallery)
  3. Browse Stock Photos (new — opens the picker)

Picker UI:

  • Modal or bottom sheet with a grid of stock photos (3 columns)
  • Filtered to the goal's current category by default
  • Category tabs at top to browse other categories
  • Tap to select → image immediately previews in the form
  • No cropping needed for stock photos (they're pre-sized)

4d. Visual Indicators

Stock photos should be subtly distinguished from user-uploaded photos:

  • In GoalForm: Small "Stock photo" badge in bottom-right corner of the image area. Disappears if user uploads their own.
  • On GoalGridCard / Dashboard: No indicator. Stock photos and user photos look identical to other users. The distinction only matters to the goal owner during editing.

4e. Community Cover Photos

Communities share the same cover image friction as goals — creators often skip the cover photo, leaving a generic placeholder. The stock photo library extends to communities with minimal additional work.

How it works:

  • Communities have categories/topics (e.g., "Fitness", "Study Groups", "Side Projects"). Map these to the same stock photo sets used for goals, plus a few community-specific additions (group settings, shared spaces).
  • On community creation: Auto-suggest a stock photo based on the community's topic/category.
  • On community edit: Same "Browse Stock Photos" option in the cover image picker.
  • Technical: Communities already accept coverImageUrl: String and coverImageFile: Upload in the CreateCommunity and UpdateCommunity mutations — same pattern as goals. No backend changes needed.

Additional community-themed photos (~10-15):

ThemePhoto ideas
General communityCampfire circle, park gathering, co-working space, round table
Fitness groupsRunning trail at dawn, outdoor boot camp area, bike rack
Study/learningLibrary reading room, stacked notebooks, chalkboard
CreativeArt supplies, music studio, pottery wheel
AccountabilityTwo journals side by side, shared calendar, team whiteboard

These supplement the category-specific goal photos. A community about "Health" would see the health goal photos plus the fitness group photos.


5. Technical Approach

Frontend Changes

FileChange
ionic_frontend/src/config/stockPhotos.tsNEW — Stock photo registry (category → photo URL mappings)
ionic_frontend/public/images/stock/NEW — ~50-80 curated WebP images
ionic_frontend/src/components/GoalForm.vueAdd stock photo auto-suggest on category change, add "Browse Stock Photos" option to image upload flow
ionic_frontend/src/components/StockPhotoPicker.vueNEW — Grid picker modal with category tabs
ionic_frontend/src/composables/useGoalForm.tsHandle stock photo URL assignment (use existing imageUrl path, no file upload)
ionic_frontend/src/components/CommunityForm.vue (or equivalent)Add stock photo auto-suggest on topic selection, add "Browse Stock Photos" to cover image flow

Backend Changes

None. The existing imageUrl: String parameter on AddGoal and UpdateGoal mutations already handles URL-based images. Stock photo URLs pass through the same pipeline as any image URL.

Data Flow

Category selected → stockPhotos[category] lookup → random pick
  → form.imageUrl = stockPhoto.url
  → mutation sends imageUrl (not file)
  → GoalMedia created with image_url string
  → Goal.image_url returns it via existing query

Image Hosting Options

Option A: Bundle with app (public/images/stock/)

  • Pros: Zero latency, works offline, no CDN cost
  • Cons: Increases app bundle by ~5-10MB, images cached in service worker
  • Best for: MVP with ~50 images

Option B: GCS bucket (same bucket as user uploads)

  • Pros: No bundle bloat, CDN-cached, easy to add/remove photos
  • Cons: Requires network, small latency on first load
  • Best for: Scaling beyond 50 images

Recommendation: Option A for MVP. The WebP images at 1200x800 will average ~50-100KB each, so 60-90 images (goals + community themes) ≈ 4-7MB. Acceptable for a mobile app.


6. Content Principles

  1. No people. User feedback was clear. Scenes, objects, environments — not faces.
  2. Aspirational but grounded. A hiking trail, not a private jet. A home-cooked meal, not a Michelin restaurant.
  3. Diverse settings. Not every health photo should be a gym. Include outdoor scenes, home workout spaces, fresh produce, water.
  4. Warm color palette. Photos should feel inviting in both light and dark mode. Avoid harsh whites or neon colors.
  5. Licensing. All photos must be royalty-free (Unsplash, Pexels, or similar license). Include credit in the config even if attribution isn't required — it's the right thing to do.

7. Out of Scope (for now)

  • Unsplash/Pexels API integration (future: dynamic library)
  • User-generated photo sharing ("use this photo from the community")
  • AI-generated cover images based on goal title
  • Photo search/filtering by keyword
  • Cropping stock photos (they ship pre-sized)

8. Open Questions

  1. Should stock photos persist as the goal's cover permanently, or should we nudge users to upload their own? A gentle nudge ("Add your own photo for a personal touch") after 7 days could work without being annoying.

  2. Should the stock photo picker be available during initial goal creation (wizard), or only in Edit Goal? The wizard is already multi-step — adding a photo grid might slow first-goal creation. Auto-suggest + edit later might be cleaner.

  3. Photo sourcing logistics. Who curates the 50-80 photos? Should we use a consistent photographer/style, or mix sources? Recommend: batch-download from Unsplash with a consistent filter (warm tone, no people, landscape) and manually curate.


  • Goals — feature doc for the shipped implementation
  • Communities — feature doc for the shipped implementation

Last updated: 2026-07-16

Loading…