@kintales/tree-view
v1.0.0
Published
A React Native family tree visualization library built on react-native-svg
Maintainers
Readme
@kintales/tree-view
Interactive family tree visualization for React Native. Pan, zoom, pinch gestures. Supports multiple marriages, step-children, half-siblings. Built on react-native-svg.
Works on iOS, Android, and Web (via react-native-web).
Installation
npm install @kintales/tree-viewPeer dependencies
npm install react-native-svg react-native-gesture-handler react-native-reanimatedFollow the installation guides for each peer dependency:
Quick start
import { FamilyTree } from '@kintales/tree-view';
const people = [
{ id: '1', name: 'Grandpa Ivan', birthYear: 1940, deathYear: 2020 },
{ id: '2', name: 'Grandma Maria', birthYear: 1943 },
{ id: '3', name: 'Father Petar', birthYear: 1965 },
{ id: '4', name: 'Mother Elena', birthYear: 1968 },
{ id: '5', name: 'Me', birthYear: 1992 },
];
const relationships = [
{ from: '1', to: '2', type: 'spouse' },
{ from: '1', to: '3', type: 'parent' },
{ from: '2', to: '3', type: 'parent' },
{ from: '3', to: '4', type: 'spouse' },
{ from: '3', to: '5', type: 'parent' },
{ from: '4', to: '5', type: 'parent' },
];
<FamilyTree
people={people}
relationships={relationships}
rootId="1"
onPersonTap={(person) => console.log('Tapped:', person.name)}
/>Features
- Automatic layout: parents above, children below, spouses side by side
- Multiple marriages with per-spouse child grouping
- Step-children, half-siblings, adopted children (all rendered identically)
- Pan, pinch-to-zoom, double-tap to reset
- Tap and long-press callbacks on nodes
- Circular photos with initials fallback
- Birth/death year labels
- Built-in themes:
warm(earth tones) andneutral(clean/modern) - Custom themes and custom node/edge renderers
- Viewport culling for large trees (50+ nodes)
- TypeScript support with full type declarations
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| people | Person[] | required | Array of people to display |
| relationships | Relationship[] | required | Array of relationships between people |
| rootId | string | first person | Center the tree on this person |
| onPersonTap | (person: Person) => void | — | Callback when a node is tapped |
| onPersonLongPress | (person: Person) => void | — | Callback on long press |
| nodeWidth | number | 120 | Width of each node in pixels |
| nodeHeight | number | 160 | Height of each node in pixels |
| horizontalSpacing | number | 40 | Horizontal gap between nodes |
| verticalSpacing | number | 80 | Vertical gap between generations |
| theme | 'warm' \| 'neutral' \| 'custom' | 'warm' | Built-in theme or custom |
| customTheme | TreeTheme | — | Custom theme object (requires theme='custom') |
| showPhotos | boolean | true | Show photo/initials circle |
| showDates | boolean | true | Show birth/death year labels |
| photoShape | 'circle' \| 'rounded' | 'circle' | Shape of photo area |
| deceasedStyle | 'dim' \| 'sepia' \| 'none' | 'none' | Visual style for deceased people |
| enablePan | boolean | true | Enable drag to pan |
| enableZoom | boolean | true | Enable pinch to zoom |
| minZoom | number | 0.3 | Minimum zoom level |
| maxZoom | number | 3.0 | Maximum zoom level |
| initialZoom | number | 1.0 | Starting zoom level |
| renderNode | (person, position) => ReactNode | — | Custom node renderer |
| renderEdge | (from, to, type) => ReactNode | — | Custom edge renderer |
Types
interface Person {
id: string;
name: string;
gender?: 'male' | 'female' | 'other';
photo?: string;
birthYear?: number;
deathYear?: number;
[key: string]: unknown;
}
interface Relationship {
from: string;
to: string;
type: 'parent' | 'spouse' | 'sibling' | 'step_parent' | 'step_child'
| 'step_sibling' | 'adopted' | 'guardian' | string;
marriageYear?: number;
divorceYear?: number;
}
interface TreeTheme {
backgroundColor: string;
nodeBackgroundColor: string;
nodeBorderColor: string;
nodeTextColor: string;
edgeColor: string;
edgeWidth: number;
fontFamily: string;
fontSize: number;
photoPlaceholderColor: string;
}Advanced: headless layout
Use the layout algorithm without rendering:
import { computeLayout } from '@kintales/tree-view';
const layout = computeLayout(people, relationships, 'root-id');
// layout.nodes: { id, person, x, y, generation }[]
// layout.edges: { fromId, toId, type, points }[]
// layout.width, layout.heightCustom node renderer
import { Circle, Text } from 'react-native-svg';
<FamilyTree
people={people}
relationships={relationships}
renderNode={(person, pos) => (
<Circle
cx={pos.x + pos.width / 2}
cy={pos.y + pos.height / 2}
r={40}
fill={person.gender === 'female' ? '#E8A0BF' : '#7286D3'}
/>
)}
/>Performance
- Trees with 50+ nodes automatically enable viewport culling
- Only visible nodes and their connecting edges are rendered
React.memoon all SVG components prevents unnecessary re-rendersuseMemoon layout computation and node/edge lists- Tested with 200+ node trees
Design principles
- All relationship types have identical visual style (no different colors/dashes for step-parent vs biological)
- No built-in gendered coloring (use
renderNodefor customization) - Deceased people have no special treatment by default (
deceasedStyle: 'none') - No assumptions about family structure (0-N spouses, 0-N parents)
- Zero opinion on data source
Examples
See the examples/ directory:
BasicTree.tsx— Minimal 3-generation treeComplexTree.tsx— Multiple marriages, half-siblings, 4 generationsLargeTree.tsx— 100+ nodes performance demoCustomTheme.tsx— Dark theme exampleCustomNodes.tsx— Custom circular node renderer
License
MIT
