npm package: @hoddy-ui/skeleton
Description: Skeleton placeholder components for Expo and React Native
Author: Hoddy inc
Latest version: 1.0.0

# @hoddy-ui/skeleton

Skeleton placeholder components for Expo and React Native. Use them to show loading states with an animated shimmer that follows the shape of your placeholder blocks.

---

## Expo SDK 54

This package is built for **Expo SDK 54**. It has been tested with:

| Dependency | Version |
|------------|--------|
| expo | ~54.0.0 |
| react-native | 0.81.x |
| react-native-reanimated | ~4.1.x |
| expo-linear-gradient | ~15.0.x |
| @react-native-masked-view/masked-view | 0.3.x |

---

## Installation

```bash
yarn add @hoddy-ui/skeleton
```

Or with npm:

```bash
npm install @hoddy-ui/skeleton
```

Peer dependencies are required. If you are using Expo SDK 54, you typically already have `react`, `react-native`, `react-native-reanimated`, `expo-linear-gradient`, and `@react-native-masked-view/masked-view` installed. If not, install them according to your Expo version.

---

## Peer dependencies

- `react` (>=18.2.0)
- `react-native` (>=0.81.0)
- `react-native-reanimated` (~4.1.x)
- `expo-linear-gradient` (~15.0.x)
- `@react-native-masked-view/masked-view` (0.3.x)
- `expo` (~54.0.0) — for Expo projects

---

## Usage

Wrap one or more `SkeletonBlock` components inside `SkeletonView`. The blocks define the shape of the shimmer; the shimmer animation is visible only where the blocks are.

```tsx
import { SkeletonView, SkeletonBlock } from "@hoddy-ui/skeleton";

function LoadingCard() {
  return (
    <SkeletonView>
      <SkeletonBlock height={20} style={{ marginBottom: 8 }} />
      <SkeletonBlock width="80%" height={16} />
      <SkeletonBlock width="60%" height={16} style={{ marginTop: 8 }} />
    </SkeletonView>
  );
}
```

---

## API reference

### SkeletonView

Container that renders an animated shimmer masked by its children. Children should be opaque shapes (e.g. `SkeletonBlock`) so the shimmer is visible only in those areas.

| Prop | Type | Description |
|------|------|-------------|
| `children` | `React.ReactNode` | Required. Typically one or more `SkeletonBlock` components. |
| `style` | `ViewStyle` | Optional. Applied to the container and mask layout. |
| `shimmerBaseColor` | `string` | Optional. Base (background) color of the shimmer. Overrides the built-in default. |
| `shimmerHighlightColor` | `string` | Optional. Highlight (moving band) color of the shimmer. Overrides the built-in default. |
| `theme` | `"light" \| "dark"` | Optional. Theme used for built-in default colors when `shimmerBaseColor` / `shimmerHighlightColor` are not set. Defaults to `"light"`. |

---

### SkeletonBlock

A single rectangular placeholder. Use only inside `SkeletonView` as a mask child. Rendered as opaque black so the parent shimmer shows through the mask in this shape.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `width` | `DimensionValue` | `"100%"` | Block width (e.g. `"80%"`, `100`, `ms(80)` if you use react-native-size-matters). |
| `height` | `DimensionValue` | `16` | Block height. |
| `borderRadius` | `number` | `6` | Corner radius. |
| `style` | `ViewStyle` | — | Optional extra styles (e.g. margin, marginBottom). |

---

### SkeletonShimmer

Internal component that renders the animated gradient. You usually do not use it directly. It is exported for advanced cases where you want to use it inside your own `MaskedView` (e.g. custom layout). In that case wrap your tree in `SkeletonProvider` and provide `value` with optional `baseColor`, `highlightColor`, and `theme`.

---

### SkeletonProvider

Context provider for shimmer colors and theme. `SkeletonView` uses it internally. Use it yourself only if you build a custom skeleton layout and need to pass colors/theme to `SkeletonShimmer`.

---

### useSkeletonColors

Hook that returns the current skeleton context value: `{ baseColor?, highlightColor?, theme? }`. Use when implementing custom components that need to read or depend on the active skeleton theme/colors.

---

### Types

- **SkeletonShimmerColors** — `{ baseColor?: string; highlightColor?: string }`
- **SkeletonTheme** — `"light" | "dark"`
- **SkeletonContextValue** — Extends `SkeletonShimmerColors` with `theme?: SkeletonTheme`

---

## Customizing colors

All default colors are defined inside this package. There is no dependency on hoddy-ui theme or any external design system.

- **Use built-in theme:** set `theme="light"` or `theme="dark"` on `SkeletonView`. Light uses `#E4E7EC` (base) and `#F7F9FC` (highlight); dark uses `#161616` and `#333`.
- **Override with custom colors:** pass `shimmerBaseColor` and/or `shimmerHighlightColor` on `SkeletonView`. These take precedence over theme defaults.

```tsx
<SkeletonView theme="dark">
  <SkeletonBlock height={24} />
</SkeletonView>

<SkeletonView
  shimmerBaseColor="#e0e0e0"
  shimmerHighlightColor="#f5f5f5"
>
  <SkeletonBlock height={24} />
</SkeletonView>
```

---

## More examples

### Card-style skeleton (avatar + lines)

```tsx
<SkeletonView style={{ padding: 16 }}>
  <View style={{ flexDirection: "row", marginBottom: 12 }}>
    <SkeletonBlock width={48} height={48} borderRadius={24} />
    <View style={{ marginLeft: 12, flex: 1 }}>
      <SkeletonBlock height={16} style={{ marginBottom: 6 }} />
      <SkeletonBlock width="70%" height={14} />
    </View>
  </View>
  <SkeletonBlock height={14} style={{ marginBottom: 6 }} />
  <SkeletonBlock width="90%" height={14} />
</SkeletonView>
```

### List row skeleton

```tsx
<SkeletonView style={{ flexDirection: "row", alignItems: "center", paddingVertical: 12 }}>
  <SkeletonBlock width={40} height={40} borderRadius={8} />
  <View style={{ marginLeft: 12, flex: 1 }}>
    <SkeletonBlock height={16} width="60%" style={{ marginBottom: 6 }} />
    <SkeletonBlock height={12} width="40%" />
  </View>
</SkeletonView>
```

### Full-screen placeholder

```tsx
<SkeletonView style={{ flex: 1, padding: 24 }}>
  <SkeletonBlock height={200} style={{ marginBottom: 24, borderRadius: 12 }} />
  <SkeletonBlock height={20} style={{ marginBottom: 8 }} />
  <SkeletonBlock height={16} style={{ marginBottom: 8 }} />
  <SkeletonBlock height={16} width="80%" />
</SkeletonView>
```

---

## License

MIT