3D carousel components for React Three Fiber
npm install @juniorxsound/react-three-components


A personal set of reusable components for React Three Fiber 🪄
> This library is a work in progress - if you find any issues, please report them here. Please proceed with caution if you use this library in production.
``bash`
npm install @juniorxsound/react-three-components
This library requires the following peer dependencies:
`bash`
npm install react react-dom three @react-three/fiber @react-spring/web @use-gesture/react
A 3D carousel that arranges items in a circle and rotates around an axis.
`tsx
import { Canvas } from "@react-three/fiber";
import {
CircularCarousel,
useCarouselContext,
} from "@juniorxsound/react-three-components";
function Item({ index }: { index: number }) {
const { activeIndex } = useCarouselContext();
const isActive = index === activeIndex;
return (
);
}
function App() {
return (
);
}
`
#### Props
| Prop | Type | Default | Description |
| ----------------- | ------------------------- | -------- | ----------------------------- |
| children | ReactNode | required | Carousel items |radius
| | number | 3 | Distance from center to items |axis
| | "x" \| "y" \| "z" | "y" | Rotation axis |index
| | number | - | Controlled active index |defaultIndex
| | number | 0 | Initial index (uncontrolled) |onIndexChange
| | (index: number) => void | - | Called when index changes |dragEnabled
| | boolean | true | Enable drag gestures |dragSensitivity
| | number | auto | Drag sensitivity |dragAxis
| | "x" \| "y" | "x" | Drag gesture axis |dragConfig
| | DragConfig | - | Additional drag options |
#### Ref Methods
`tsx
const ref = useRef
ref.current.next(); // Go to next item
ref.current.prev(); // Go to previous item
ref.current.goTo(2); // Go to specific index
`
#### With Navigation Triggers
`tsx
`
---
A carousel that slides items linearly (horizontally or vertically).
`tsx
import { Canvas } from "@react-three/fiber";
import {
LinearCarousel,
useLinearCarouselContext,
} from "@juniorxsound/react-three-components";
function Item({ index }: { index: number }) {
const { activeIndex } = useLinearCarouselContext();
const isActive = index === activeIndex;
return (
);
}
function App() {
return (
);
}
`
#### Props
| Prop | Type | Default | Description |
| ----------------- | ---------------------------- | -------------- | ---------------------------------- |
| children | ReactNode | required | Carousel items |gap
| | number | 0.2 | Space between items |direction
| | "horizontal" \| "vertical" | "horizontal" | Slide direction |index
| | number | - | Controlled active index |defaultIndex
| | number | 0 | Initial index (uncontrolled) |onIndexChange
| | (index: number) => void | - | Called when index changes |dragEnabled
| | boolean | true | Enable drag gestures |dragSensitivity
| | number | 150 | Drag sensitivity |dragAxis
| | "x" \| "y" | auto | Drag axis (derived from direction) |dragConfig
| | DragConfig | - | Additional drag options |
#### Ref Methods
`tsx
const ref = useRef
ref.current.next(); // Go to next item (bounded)
ref.current.prev(); // Go to previous item (bounded)
ref.current.goTo(2); // Go to specific index
`
> Note: LinearCarousel is bounded (doesn't wrap around), unlike CircularCarousel which loops infinitely.
#### With Navigation Triggers
`tsx
`
---
Parse and assign KHR_materials_variants on an already-loaded glTF. Load the model with useGLTF (Drei), useLoader(GLTFLoader, url), or your own loader; then pass the result. The hook applies the first variant initially (or pass { variant: "name" } to avoid flashing). Returns variants, activeVariant, and setVariant. Suspends until the variant is applied—wrap in a boundary.
`tsx
import { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { useGLTF } from "@react-three/drei";
import { useGLTFMaterialVariants } from "@juniorxsound/react-three-components";
function Shoe() {
const gltf = useGLTF("/MaterialsVariantsShoe.gltf");
const { variants, activeVariant, setVariant } = useGLTFMaterialVariants(
gltf,
{ variant: "midnight" }
);
return (
{/ Use setVariant(name) to switch variants /}
);
}
function App() {
return (
);
}
`
| Return | Type | Description |
| ------------- | ---------- | ------------------------------------ |
| variants | string[] | Variant names from the extension. |activeVariant
| | string \| null | Currently active variant name. |setVariant
| | (name: string) => void | Switch to a variant by name. |
---
Access carousel state from any child component:
`tsx
// For CircularCarousel
import { useCarouselContext } from "@juniorxsound/react-three-components";
const { activeIndex, count, next, prev, goTo } = useCarouselContext();
// For LinearCarousel
import { useLinearCarouselContext } from "@juniorxsound/react-three-components";
const { activeIndex, count, next, prev, goTo } = useLinearCarouselContext();
`
Fine-tune drag behavior:
`tsx`
axis: "x", // Constrain to axis
threshold: 10, // Pixels before drag starts
rubberband: 0.2, // Elastic effect at bounds
touchAction: "pan-y", // CSS touch-action
pointer: { touch: true }, // Pointer options
}}
>
`bash`
nvm use # Switch to Node 24 (uses .nvmrc)
npm install # Install dependencies
npm run dev # Start Storybook dev server
npm run test # Run tests in watch mode
npm run lint # Run ESLint
npm run build # Build the library
This package uses npm trusted publishers for secure, token-free publishing.
`bash``
npm run release:patch # 0.1.0 → 0.1.1
npm run release:minor # 0.1.0 → 0.2.0
npm run release:major # 0.1.0 → 1.0.0
Then create a GitHub Release from the tag - this triggers automatic npm publish with provenance.
MIT