Skip to content

Repository files navigation

Open Family Tree

A private, invite-only family tree web app you can self-host. Explore an interactive family tree, view member profiles, and manage your family's history — completely under your own control, independent of Ancestry or any subscription service.

Built with: Next.js · Supabase · Tailwind CSS · Vercel


Features

  • Interactive visual tree (desktop) + accordion directory (mobile)
  • Member profiles — photos, birth/death dates, location, fun facts
  • Magic-link login — no passwords required
  • Admin dashboard — add people, manage relationships, invite members
  • Family map — plot all members on an interactive map (OpenStreetMap, free)
  • Find Connection — see how any two people are related (BFS traversal)
  • GEDCOM export/import — compatible with Ancestry, FamilySearch, Gramps
  • Family data collection form — shareable link for members to submit their own info
  • Email notifications — contact form + login alerts via Resend
  • PDF/print export — full tree or individual branches

Quick Start

1. Clone and install

git clone https://github.com/your-username/open-family-tree.git
cd open-family-tree
npm install

2. Set up Supabase

  1. Create a free project at supabase.com
  2. Run the SQL in Database Setup below
  3. Copy your Project URL and anon key from Project Settings → API

3. Set up Resend (email)

  1. Create a free account at resend.com
  2. Copy your API key

4. Configure environment variables

cp .env.example .env.local

Fill in .env.local with your values (see Environment Variables).

5. Add your family crest / logo

Replace public/crest.svg with your own image, or upload it anywhere and set NEXT_PUBLIC_CREST_URL to that URL.

6. Run locally

npm run dev

Open http://localhost:3000.

7. Deploy to Vercel

  1. Push your repo to GitHub
  2. Import it at vercel.com/new
  3. Add all environment variables in Vercel → Settings → Environment Variables
  4. Deploy

Environment Variables

Copy .env.example to .env.local and fill in your values:

Variable Example Purpose
NEXT_PUBLIC_SUPABASE_URL https://abc.supabase.co Supabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEY eyJ... Supabase anon key
SUPABASE_SERVICE_ROLE_KEY eyJ... Supabase service role key (server-side only)
RESEND_API_KEY re_... Resend API key for email
NEXT_PUBLIC_FAMILY_NAME Smith Family Tree App title shown everywhere
NEXT_PUBLIC_FAMILY_SHORT_NAME Smith Short name for descriptions
NEXT_PUBLIC_ROOT_PRIORITY_NAMES John Smith,Jane Smith Full names that appear first in the tree (comma-separated)
NEXT_PUBLIC_ROOT_FAMILY_NAME Smith Primary family surname — shown before other branches
NEXT_PUBLIC_CREST_URL /crest.svg Path or URL to your family crest/logo
ADMIN_EMAIL admin@example.com Where contact form and login notifications go

Database Setup

Run these in the Supabase SQL Editor (Database → SQL Editor):

Tables

CREATE TABLE people (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  first_name text NOT NULL,
  last_name text NOT NULL,
  date_of_birth text,
  dob_precision text,
  date_of_death text,
  dod_precision text,
  is_deceased boolean NOT NULL DEFAULT false,
  address text,
  lat double precision,
  lng double precision,
  fun_fact text,
  photo_url text,
  gender text,
  birth_order integer,
  created_by text,
  created_at timestamptz DEFAULT now()
);

CREATE TABLE relationships (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id uuid REFERENCES people(id) ON DELETE CASCADE,
  related_person_id uuid REFERENCES people(id) ON DELETE CASCADE,
  relationship_type text NOT NULL
);

CREATE TABLE user_profiles (
  id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  email text,
  role text NOT NULL DEFAULT 'member',
  person_id uuid REFERENCES people(id),
  created_at timestamptz DEFAULT now()
);

CREATE TABLE app_settings (
  key text PRIMARY KEY,
  value text NOT NULL
);

CREATE TABLE collection_links (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  token text UNIQUE NOT NULL,
  active boolean NOT NULL DEFAULT true,
  created_at timestamptz DEFAULT now()
);

CREATE TABLE family_collections (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  token text NOT NULL,
  submitted_by_name text,
  status text NOT NULL DEFAULT 'pending',
  data jsonb,
  created_at timestamptz DEFAULT now()
);

Row Level Security

ALTER TABLE people ENABLE ROW LEVEL SECURITY;
ALTER TABLE relationships ENABLE ROW LEVEL SECURITY;
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE app_settings ENABLE ROW LEVEL SECURITY;

CREATE POLICY "auth_read_people" ON people FOR SELECT TO authenticated USING (true);
CREATE POLICY "auth_read_relationships" ON relationships FOR SELECT TO authenticated USING (true);

CREATE POLICY "auth_read_profiles" ON user_profiles FOR SELECT TO authenticated USING (true);
CREATE POLICY "auth_insert_own_profile" ON user_profiles FOR INSERT TO authenticated WITH CHECK (auth.uid() = id);
CREATE POLICY "auth_update_own_profile" ON user_profiles FOR UPDATE TO authenticated USING (auth.uid() = id);
CREATE POLICY "admin_update_any_profile" ON user_profiles FOR UPDATE TO authenticated
  USING ((SELECT role FROM user_profiles WHERE id = auth.uid()) = 'admin');

CREATE POLICY "auth_read_settings" ON app_settings FOR SELECT TO authenticated USING (true);
CREATE POLICY "admin_write_settings" ON app_settings FOR ALL TO authenticated
  USING ((SELECT role FROM user_profiles WHERE id = auth.uid()) = 'admin')
  WITH CHECK ((SELECT role FROM user_profiles WHERE id = auth.uid()) = 'admin');

Storage (profile photos)

INSERT INTO storage.buckets (id, name, public) VALUES ('photos', 'photos', true);

CREATE POLICY "auth_upload_photos" ON storage.objects FOR INSERT TO authenticated
  WITH CHECK (bucket_id = 'photos');

CREATE POLICY "public_read_photos" ON storage.objects FOR SELECT TO public
  USING (bucket_id = 'photos');

First Login

  1. Open your deployed app and enter your email on the login page
  2. Click the magic link in your email
  3. You are automatically made admin — the first person to sign in gets admin role
  4. Go to Admin → Members to invite family members by email

Tech Stack

Layer Technology
Framework Next.js (App Router) + TypeScript
Database Supabase (PostgreSQL)
Auth Supabase Auth (magic links)
Storage Supabase Storage (photos)
Email Resend API
Maps Leaflet 1.9 + OpenStreetMap (free, no API key needed)
Geocoding Nominatim / OpenStreetMap (free, no API key needed)
Styling Tailwind CSS
Hosting Vercel (free tier sufficient for a family)

Total infrastructure cost: $0 on free tiers for a family-scale deployment.


License

MIT

About

Self-hostable private family tree web app — Next.js, Supabase, Tailwind. Configure with env vars, no code changes needed.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages