An interactive, lightweight React component library for visualizing and selecting body muscles. Perfect for fitness apps, workout planners, physiotherapy tools, and anatomy education.
- π― Dual Interaction Modes: Click-based selection or programmatic control
- π Smart Muscle Pairing: Automatically sync left/right muscle groups
- π¨ Customizable Styling: Override colors, opacity, and visual states
- π± Responsive: Works on desktop and mobile
- πͺΆ Lightweight: Zero dependencies (just React)
- π Type-Safe: Full TypeScript support
- π³ Tree-Shakeable: Import only what you need
- βΏ Accessible: Proper hover states and keyboard support
npm install dynamic-body-muscle-map
# or
yarn add dynamic-body-muscle-map
# or
pnpm add dynamic-body-muscle-mapimport { BodyMap, useMuscleState } from 'dynamic-body-muscle-map';
function App() {
const { selected, toggleSelect } = useMuscleState();
return (
<BodyMap
selected={selected}
interactive={true}
onMuscleClick={toggleSelect}
/>
);
}The main component for rendering the interactive body muscle map.
| Prop | Type | Default | Description |
|---|---|---|---|
highlighted |
MuscleId[] |
[] |
Array of muscle IDs to highlight |
selected |
MuscleId[] |
[] |
Array of muscle IDs to mark as selected |
interactive |
boolean | MuscleId[] |
true |
Enable click interaction (true/false or array of specific muscles) |
onMuscleClick |
(id: MuscleId) => void |
- | Callback when a muscle is clicked |
onMuscleHover |
(id: MuscleId | null) => void |
- | Callback when a muscle is hovered |
showHitLayers |
boolean |
false |
Show invisible hit detection layers (debug) |
styles |
MuscleStyles |
Default styles | Custom styles for different muscle states |
className |
string |
'' |
Additional CSS class for the SVG container |
<BodyMap
highlighted={['RightBicep', 'LeftBicep']}
selected={['RightQuad']}
interactive={['RightBicep', 'LeftBicep', 'Chest']}
onMuscleClick={(id) => console.log('Clicked:', id)}
onMuscleHover={(id) => console.log('Hovered:', id)}
showHitLayers={false}
styles={{
highlighted: { fill: '#ff6b6b', opacity: 0.8 },
selected: { fill: '#4ecdc4', opacity: 0.9 },
}}
/>A React hook for managing muscle selection state.
| Property | Type | Description |
|---|---|---|
highlighted |
MuscleId[] |
Currently highlighted muscles |
selected |
MuscleId[] |
Currently selected muscles |
toggleHighlight |
(id: MuscleId) => void |
Toggle highlight for a single muscle |
highlightMuscles |
(ids: MuscleId[]) => void |
Set highlighted muscles (replaces existing) |
clearHighlights |
() => void |
Clear all highlights |
toggleSelect |
(id: MuscleId) => void |
Toggle selection for a single muscle |
selectMuscles |
(ids: MuscleId[]) => void |
Set selected muscles (replaces existing) |
clearSelections |
() => void |
Clear all selections |
reset |
() => void |
Clear both highlights and selections |
const {
selected,
toggleSelect,
selectMuscles,
clearSelections,
} = useMuscleState();
// Toggle individual muscle
<button onClick={() => toggleSelect('RightBicep')}>
Toggle Right Bicep
</button>
// Select multiple muscles
<button onClick={() => selectMuscles(['RightBicep', 'LeftBicep'])}>
Select Both Biceps
</button>
// Clear all
<button onClick={clearSelections}>Clear All</button>Override default styles for different muscle states:
const customStyles = {
default: {
fill: '#c2c2c2',
opacity: 1,
},
highlighted: {
fill: '#ff0000',
opacity: 0.8,
stroke: '#000000',
strokeWidth: 2,
},
selected: {
fill: '#00ff00',
opacity: 0.9,
stroke: '#ffffff',
strokeWidth: 3,
},
hovered: {
fill: '#0000ff',
opacity: 0.85,
},
disabled: {
fill: '#999999',
opacity: 0.5,
},
};
<BodyMap styles={customStyles} />;Get muscle data by ID.
import { getMuscleById } from 'dynamic-body-muscle-map';
const muscle = getMuscleById('RightBicep');
console.log(muscle.name); // "Right Bicep"
console.log(muscle.group); // "RightArm"Get all muscles in a group.
import { getMusclesByGroup } from 'dynamic-body-muscle-map';
const armMuscles = getMusclesByGroup('RightArm');
console.log(armMuscles); // [{ id: 'RightBicep', ... }, ...]Get the paired muscle (left β right).
import { getPairedMuscle } from 'dynamic-body-muscle-map';
const paired = getPairedMuscle('RightBicep');
console.log(paired); // "LeftBicep"import { BodyMap, useMuscleState } from 'dynamic-body-muscle-map';
function InteractiveExample() {
const { selected, toggleSelect, clearSelections } = useMuscleState();
return (
<div>
<BodyMap
selected={selected}
interactive={true}
onMuscleClick={toggleSelect}
/>
<button onClick={clearSelections}>Clear Selection</button>
<p>Selected: {selected.join(', ')}</p>
</div>
);
}import { BodyMap } from 'dynamic-body-muscle-map';
import { useState } from 'react';
function ProgrammaticExample() {
const [highlighted, setHighlighted] = useState(['RightBicep', 'LeftBicep']);
return (
<div>
<BodyMap highlighted={highlighted} interactive={false} />
<button onClick={() => setHighlighted(['Chest', 'AbsUpper'])}>
Highlight Chest & Abs
</button>
</div>
);
}import { BodyMap, useMuscleState } from 'dynamic-body-muscle-map';
function MixedExample() {
const { selected, toggleSelect } = useMuscleState();
const [highlighted] = useState(['Chest', 'AbsUpper']);
return (
<BodyMap
highlighted={highlighted}
selected={selected}
interactive={['RightBicep', 'LeftBicep']} // Only biceps clickable
onMuscleClick={toggleSelect}
/>
);
}import {
BodyMap,
useMuscleState,
getPairedMuscle,
} from 'dynamic-body-muscle-map';
function PairingExample() {
const { selected, toggleSelect } = useMuscleState();
const [syncPairs, setSyncPairs] = useState(true);
const handleMuscleClick = (muscleId) => {
toggleSelect(muscleId);
if (syncPairs) {
const paired = getPairedMuscle(muscleId);
if (paired) toggleSelect(paired);
}
};
return (
<div>
<label>
<input
type='checkbox'
checked={syncPairs}
onChange={(e) => setSyncPairs(e.target.checked)}
/>
Sync Muscle Pairs
</label>
<BodyMap
selected={selected}
interactive={true}
onMuscleClick={handleMuscleClick}
/>
</div>
);
}graph TD
A[BodyMap] --> B[MuscleGroup]
B --> C[Muscle]
C --> D[Visible Path]
C --> E[Hit Layer Path]
A --> F[useMuscleState Hook]
F --> G[State Management]
A --> H[MUSCLE_GROUPS Data]
H --> I[muscleData.ts]
style A fill:#4ecdc4
style F fill:#ff6b6b
style H fill:#1E5474FF
sequenceDiagram
participant User
participant BodyMap
participant Muscle
participant State
User->>BodyMap: Click muscle
BodyMap->>Muscle: Detect click
Muscle->>BodyMap: Emit onMuscleClick(id)
BodyMap->>State: toggleSelect(id)
State->>BodyMap: Update selected[]
BodyMap->>Muscle: Re-render with new state
Muscle->>User: Visual feedback
stateDiagram-v2
[*] --> Idle
Idle --> Highlighted: highlightMuscles()
Idle --> Selected: toggleSelect()
Highlighted --> Selected: toggleSelect()
Selected --> Highlighted: clearSelections()
Highlighted --> Idle: clearHighlights()
Selected --> Idle: clearSelections()
Idle --> Hovered: Mouse Enter
Hovered --> Idle: Mouse Leave
BicepsForearmsShoulders
ChestAbsUpperAbsLowerAbsObliques
QuadsCalfsAdductors
- Place your SVG in
src/assets/body-map-[gender]_[view].svg - Run the parser:
npx tsx scripts/parse-svg.ts - Generated files:
src/types/muscles.ts- TypeScript typessrc/utils/muscleData.ts- Muscle data & pairs
- Each muscle should be a
<path>element with a uniqueid - Optional: Add
[MuscleID]_Hitpaths for better click detection - Group muscles using
<g id="[GroupName]Group">
Example:
<g id="RightArmGroup">
<path id="RightBicep" d="M..." fill="#c2c2c2" />
<path id="RightBicep_Hit" d="M..." fill="transparent" opacity="0" />
</g>| Package | Size (gzipped) |
|---|---|
| Core Component | ~8 KB |
| Full Library | ~15 KB |
- Back view support
- Male/Female body variations
- Animation transitions
- Preset workout configurations
- Export/Import muscle selections
- Touch gestures for mobile
- Accessibility improvements (ARIA labels)
Contributions are welcome! Please read our Contributing Guide for details. //Still pending at the moment...
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
MIT Β© Joseph Scafidi
- SVG assets based on anatomical references
- Built with React, TypeScript, and Vite
- Inspired by fitness and physiotherapy applications
- π Report a Bug
- π‘ Request a Feature
- π§ Email: jscafidi616@hotmail.com
Made with β€οΈ for the fitness and health community

