Overview
Baby profiles store essential information about your baby and configure how the app tracks and displays data. Each profile includes demographic information, timezone settings, measurement preferences, and more.
Schema Structure
Baby profiles are stored in the babyProfiles table:
babyProfiles : defineTable ({
familyId: v . optional ( v . id ( "families" )),
name: v . string (),
dob: v . string (),
gender: v . optional ( v . string ()),
timezone: v . string (),
measurementUnits: v . optional ( v . object ({
volume: v . optional ( v . string ()),
weight: v . optional ( v . string ()),
length: v . optional ( v . string ()),
})),
createdAt: v . string (),
})
. index ( "by_createdAt" , [ "createdAt" ])
. index ( "by_familyId" , [ "familyId" ])
Core Fields
Baby’s name, displayed throughout the app
Date of birth in ISO format (YYYY-MM-DD). Used to calculate age and age-appropriate milestones
Optional gender value: "boy", "girl", or "other". Controls theming and pronouns throughout the app
IANA timezone (e.g., "Asia/Kolkata", "America/New_York"). Defaults to "Asia/Kolkata"
Reference to the family this profile belongs to. Required for multi-user access
Measurement Units
Customize units for different measurements:
measurementUnits : {
volume : "ml" | "oz" , // Feed volumes
weight : "kg" | "lb" , // Body weight
length : "cm" | "in" // Height measurements
}
While the schema supports measurement unit preferences, the current implementation primarily uses metric units (ml, kg, cm). Imperial unit conversion is planned for future releases.
Creating a Profile
Use the createBabyProfile mutation:
import { useMutation } from "convex/react" ;
import { api } from "../../convex/_generated/api" ;
const createProfile = useMutation ( api . events . createBabyProfile );
await createProfile ({
familyId ,
name: "Emma" ,
dob: "2024-01-15" ,
gender: "girl" ,
timezone: "America/New_York" ,
measurementUnits: {
volume: "oz" ,
weight: "lb" ,
length: "in"
}
});
When a profile is created, the system automatically creates a caregiver record for the family owner.
Updating a Profile
Modify profile settings:
export const updateBabyProfile = mutation ({
args: {
id: v . id ( "babyProfiles" ),
name: v . optional ( v . string ()),
dob: v . optional ( v . string ()),
gender: v . optional ( v . string ()),
timezone: v . optional ( v . string ()),
measurementUnits: v . optional ( v . object ({
volume: v . optional ( v . string ()),
weight: v . optional ( v . string ()),
length: v . optional ( v . string ()),
})),
},
handler : async ( ctx , args ) => {
const user = await requireAuth ( ctx );
await requireBabyAccess ( ctx , args . id , user . _id );
const { id , ... updates } = args ;
await ctx . db . patch ( id , updates );
return id ;
},
});
Querying Profiles
Get Active Profile
const profile = useQuery ( api . events . getBabyProfile , { id: babyId });
List All Profiles
const profiles = useQuery ( api . events . getBabyProfiles );
The query returns profiles from all families the user belongs to, sorted by creation date (newest first).
Gender Theming
The gender field controls visual theming throughout the app:
import { useGenderTheme } from "@/components/GenderTheme" ;
const genderTheme = useGenderTheme ();
// Returns theme objects like:
// {
// primary: "text-sage",
// primaryLight: "bg-sage/10",
// bg: "bg-sage/5",
// border: "border-sage/20",
// text: "text-sage"
// }
Themes available:
Boy : Blue tones
Girl : Sage green tones
Other/Unspecified : Neutral tones
Caregiver Management
Each baby profile can have multiple caregivers who log activities:
caregivers : defineTable ({
babyId: v . id ( "babyProfiles" ),
displayName: v . string (),
color: v . string (),
userId: v . optional ( v . string ()),
createdAt: v . string (),
})
. index ( "by_babyId" , [ "babyId" ])
. index ( "by_userId" , [ "userId" ])
Caregiver Colors
Each caregiver gets a unique color for easy visual identification:
const DEFAULT_CAREGIVER_COLORS = [
"#7C9A82" , // Sage
"#C4A484" , // Clay
"#6B8CAE" , // Dusty Blue
"#E57373" , // Coral
"#9C7CF4" , // Purple
"#F4B942" , // Gold
"#4DB6AC" , // Teal
"#7986CB" , // Periwinkle
];
Adding Caregivers
const addCaregiver = useMutation ( api . events . createCaregiver );
await addCaregiver ({
babyId ,
displayName: "Grandma" ,
color: "#7C9A82"
});
Listing Caregivers
const caregivers = useQuery ( api . events . listCaregivers , { babyId });
Family Association
Profiles belong to families, enabling multi-user collaboration:
Family Created
A family record is created with an owner
Profile Linked
Baby profile is associated with the family via familyId
Access Control
All family members can access the profile and log activities
Access Control
The requireBabyAccess helper ensures users can only access profiles they have permission for:
export async function requireBabyAccess (
ctx : QueryCtx | MutationCtx ,
babyId : Id < "babyProfiles" >,
userId : string
) {
const profile = await ctx . db . get ( babyId );
if ( ! profile || ! profile . familyId ) {
throw new Error ( "Baby profile not found" );
}
const familyIds = await getUserFamilyIds ( ctx , userId );
if ( ! familyIds . includes ( profile . familyId )) {
throw new Error ( "Not a member of this family" );
}
}
Timezone Handling
Timezone settings affect:
Daily aggregate calculations
Reminder scheduling
Date boundaries for reports
Display of timestamps
Changing timezone after logging significant data may affect historical reports and aggregates. The app uses the profile’s current timezone for all calculations.
Age Calculation
The baby’s age is calculated from the dob field:
import { formatBabyAge } from "@/lib/time" ;
const age = formatBabyAge ( profile . dob );
// Returns: "2 months 15 days"
Age affects:
Milestone suggestions
Growth chart percentiles
Feeding recommendations
Sleep pattern expectations
Activity Tracking All activities are linked to a baby profile
Milestones Age-appropriate milestone suggestions
Reminders Profile-specific reminder rules
Weekly Digests Profile-based analytics and insights