Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/blue-news-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"react-native-reanimated-carousel": minor
---

- add `itemWidth`/`itemHeight` props so horizontal and vertical carousels can define their snapping step explicitly (e.g. to show multiple cards per page)
- default behaviour still falls back to the carousel container size or legacy `width`/`height` props
29 changes: 29 additions & 0 deletions .changeset/odd-news-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
"react-native-reanimated-carousel": minor
---

## ✨ Style API refresh

- `style` now controls the outer carousel container (positioning, width/height, margins).
- New `contentContainerStyle` replaces `containerStyle` for styling the scrollable content.
- `width` and `height` props are deprecated; define size via `style` instead.

### Migration Example

```tsx
// Before
<Carousel
width={300}
height={200}
containerStyle={{ paddingHorizontal: 16 }}
/>

// After
<Carousel
style={{ width: 300, height: 200 }}
contentContainerStyle={{ paddingHorizontal: 16 }}
/>
```

- Any layout logic still works; simply move `width`/`height` into `style` and container tweaks into `contentContainerStyle`.
- `contentContainerStyle` runs on the JS thread—avoid adding `opacity` / `transform` there if you rely on built-in animations.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,7 @@ coverage/
.issue-tasks/

# Agents Docs
*.local.md
*.local.md

# Claude
CLAUDE.md
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,37 @@

- [#850](https://github.com/dohooo/react-native-reanimated-carousel/pull/850) [`9b388e6`](https://github.com/dohooo/react-native-reanimated-carousel/commit/9b388e6f6237126c4ed25c2442c4b788aad7adf6) Thanks [@dohooo](https://github.com/dohooo)! - # 🎯 Support for Expo 54 & Dynamic Sizing

- Style props refactoring:

- **BREAKING CHANGE**: The styling props have been refactored for clarity and consistency with React Native's `ScrollView`.
- **DEPRECATED**: `width` and `height` props are now deprecated. Please use the `style` prop to define component size.
- **NEW**: The `style` prop now controls the outer container's style and is the primary way to set `width` and `height`.
- **NEW**: A new `contentContainerStyle` prop has been added to control the style of the inner scrollable content.

#### Migration Guide

**Before:**
```jsx
<Carousel
width={300}
height={200}
style={{ backgroundColor: 'red' }} // Applied to inner container
containerStyle={{ margin: 10 }} // Applied to outer container
/>
```

**After:**
```jsx
<Carousel
style={{
width: 300,
height: 200,
margin: 10
}} // Applied to outer container
contentContainerStyle={{ backgroundColor: 'red' }} // Applied to inner container
/>
```

## ✨ Major Features

### Dynamic Sizing Support
Expand Down
4 changes: 1 addition & 3 deletions example/app/app/demos/basic-layouts/left-align/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ function Index() {
<View id="carousel-component" dataSet={{ kind: "basic-layouts", name: "left-align" }}>
<Carousel
loop={true}
width={430}
height={258}
snapEnabled={true}
pagingEnabled={true}
autoPlayInterval={2000}
data={defaultDataWith6Colors}
style={{ width: "100%" }}
style={{ width: 430, height: 258 }}
onSnapToItem={(index) => console.log("current index:", index)}
renderItem={renderItem({ rounded: true, style: { marginRight: 8 } })}
/>
Expand Down
21 changes: 15 additions & 6 deletions example/app/app/demos/basic-layouts/left-align/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { CaptureWrapper } from "@/store/CaptureProvider";
import { renderItem } from "@/utils/render-item";
import * as React from "react";
import { View } from "react-native";
import type { ICarouselInstance } from "react-native-reanimated-carousel";
import type { StyleProp, ViewStyle } from "react-native";
import type { ICarouselInstance, TCarouselProps } from "react-native-reanimated-carousel";
import Carousel from "react-native-reanimated-carousel";

function Index() {
Expand All @@ -18,23 +19,31 @@ function Index() {
autoPlayInterval: 2000,
autoPlayReverse: false,
data: defaultDataWith6Colors,
height: 258,
loop: true,
pagingEnabled: true,
snapEnabled: true,
vertical: false,
width: window.width,
},
});

const {
width: _ignoredWidth,
height: _ignoredHeight,
...restSettings
} = advancedSettings as TCarouselProps;

return (
<View style={{ flex: 1 }}>
<CaptureWrapper>
<Carousel
{...advancedSettings}
{...restSettings}
ref={ref}
style={{ width: "100%" }}
width={constants.PAGE_WIDTH * 0.75}
style={{ width: window.width, height: 258 }}
contentContainerStyle={{
width: window.width / 2,
height: 258,
overflow: "visible",
}}
onSnapToItem={(index) => console.log("current index:", index)}
renderItem={renderItem({ rounded: true, style: { marginRight: 8 } })}
/>
Expand Down
6 changes: 3 additions & 3 deletions example/app/app/demos/basic-layouts/normal/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import Carousel from "react-native-reanimated-carousel";

import { window } from "@/constants/sizes";

const defaultDataWith6Colors = ["#B0604D", "#899F9C", "#B3C680", "#5C6265", "#F5D399", "#F1F1F1"];

function Index() {
Expand All @@ -14,14 +16,12 @@ function Index() {
<Carousel
testID={"xxx"}
loop={true}
width={430}
height={258}
snapEnabled={true}
pagingEnabled={true}
autoPlayInterval={2000}
data={defaultDataWith6Colors}
defaultScrollOffsetValue={scrollOffsetValue}
style={{ width: "100%" }}
style={{ width: window.width, height: 258 }}
onScrollStart={() => {
console.log("Scroll start");
}}
Expand Down
10 changes: 6 additions & 4 deletions example/app/app/demos/basic-layouts/normal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { useAdvancedSettings } from "@/hooks/useSettings";
import { CaptureWrapper } from "@/store/CaptureProvider";
import { renderItem } from "@/utils/render-item";
import * as React from "react";
import type { StyleProp, ViewStyle } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import type { ICarouselInstance } from "react-native-reanimated-carousel";
import type { ICarouselInstance, TCarouselProps } from "react-native-reanimated-carousel";
import Carousel from "react-native-reanimated-carousel";
import { Stack } from "tamagui";

Expand All @@ -20,12 +21,10 @@ function Index() {
autoPlayInterval: 2000,
autoPlayReverse: false,
data: defaultDataWith6Colors,
height: 258,
loop: true,
pagingEnabled: true,
snapEnabled: true,
vertical: false,
width: window.width,
},
});

Expand All @@ -37,7 +36,10 @@ function Index() {
ref={ref}
defaultScrollOffsetValue={scrollOffsetValue}
testID={"xxx"}
style={{ width: "100%" }}
style={{
height: 258,
width: window.width,
}}
onScrollStart={() => {
console.log("Scroll start");
}}
Expand Down
7 changes: 4 additions & 3 deletions example/app/app/demos/basic-layouts/parallax/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@ function Index() {
<Carousel
autoPlayInterval={2000}
data={defaultDataWith6Colors}
height={258}
loop={true}
pagingEnabled={true}
snapEnabled={true}
width={window.width}
style={{
width: window.width,
height: 258,
}}
mode="parallax"
modeConfig={{
parallaxScrollingScale: 0.9,
parallaxScrollingOffset: 50,
}}
onProgressChange={progress}
onProgressChange={(offsetProgress, absoluteProgress) => {
progress.value = absoluteProgress;
}}
renderItem={renderItem({ rounded: true })}
/>
</View>
Expand Down
7 changes: 4 additions & 3 deletions example/app/app/demos/basic-layouts/parallax/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ function Index() {
autoPlayInterval: 2000,
autoPlayReverse: false,
data: defaultDataWith6Colors,
height: 258,
loop: true,
pagingEnabled: true,
snapEnabled: true,
vertical: false,
width: PAGE_WIDTH,
},
});

Expand All @@ -40,13 +38,16 @@ function Index() {
{...advancedSettings}
style={{
width: PAGE_WIDTH,
height: 258,
}}
mode="parallax"
modeConfig={{
parallaxScrollingScale: 0.9,
parallaxScrollingOffset: 50,
}}
onProgressChange={progress}
onProgressChange={(offsetProgress, absoluteProgress) => {
progress.value = absoluteProgress;
}}
renderItem={renderItem({ rounded: true })}
/>
</CaptureWrapper>
Expand Down
6 changes: 2 additions & 4 deletions example/app/app/demos/basic-layouts/stack/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@ function Index() {
ref={ref}
autoPlayInterval={2000}
data={defaultDataWith6Colors}
height={220}
loop={true}
pagingEnabled={true}
snapEnabled={true}
width={430 * 0.75}
style={{
width: 430 * 0.75,
height: 220,
alignItems: "center",
justifyContent: "center",
width: "100%",
height: 240,
}}
mode={"horizontal-stack"}
modeConfig={{
Expand Down
40 changes: 30 additions & 10 deletions example/app/app/demos/basic-layouts/stack/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";
import { View } from "react-native";
import Carousel, { ICarouselInstance } from "react-native-reanimated-carousel";
import type { StyleProp, ViewStyle } from "react-native";
import Carousel, { ICarouselInstance, TCarouselProps } from "react-native-reanimated-carousel";

import { CustomSelectActionItem } from "@/components/ActionItems";
import { CarouselAdvancedSettingsPanel } from "@/components/CarouselAdvancedSettingsPanel";
Expand Down Expand Up @@ -31,20 +32,39 @@ function Index() {
},
});

const {
width,
height,
style: advancedStyle,
...restSettings
} = advancedSettings as {
width?: number;
height?: number;
style?: StyleProp<ViewStyle>;
} & TCarouselProps;

const baseDimensions = React.useMemo(() => {
if (typeof width === "number" || typeof height === "number") {
return { width, height };
}
return { width: 280, height: 210 };
}, [width, height]);

const mergedStyle = React.useMemo<StyleProp<ViewStyle>>(
() =>
[baseDimensions, { alignItems: "center", justifyContent: "center" }, advancedStyle].filter(
Boolean
) as StyleProp<ViewStyle>[],
[baseDimensions, advancedStyle]
);

return (
<View style={{ flex: 1 }}>
<CaptureWrapper>
<Carousel
ref={ref}
{...advancedSettings}
style={{
alignItems: "center",
justifyContent: "center",
width: "100%",
height: 240,
}}
width={280}
height={210}
{...restSettings}
style={mergedStyle}
mode={mode}
modeConfig={{
snapDirection,
Expand Down
22 changes: 17 additions & 5 deletions example/app/app/demos/custom-animations/advanced-parallax/demo.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import * as React from "react";
import { View } from "react-native";
import Animated, { interpolate, interpolateColor, useAnimatedStyle } from "react-native-reanimated";
import Animated, {
Extrapolation,
interpolate,
interpolateColor,
useAnimatedStyle,
} from "react-native-reanimated";
import type { SharedValue } from "react-native-reanimated";
import Carousel, { TAnimationStyle } from "react-native-reanimated-carousel";

Expand Down Expand Up @@ -47,10 +52,17 @@ const CustomItem: React.FC<ItemProps> = ({ index, animationValue }) => {
);
};
function Index() {
const animationStyle: TAnimationStyle = React.useCallback((value: number) => {
const [isAutoPlay, setIsAutoPlay] = React.useState(false);

const animationStyle: TAnimationStyle = React.useCallback((value: number, index: number) => {
"worklet";

const zIndex = interpolate(value, [-1, 0, 1], [10, 20, 30]);
const zIndex = interpolate(
value,
value > 0 ? [0, 1] : [-1, 0],
value > 0 ? [10, 20] : [-10, 0],
Extrapolation.CLAMP
);
const translateX = interpolate(value, [-2, 0, 1], [-PAGE_WIDTH, 0, PAGE_WIDTH]);

return {
Expand All @@ -66,9 +78,9 @@ function Index() {
>
<CaptureWrapper>
<Carousel
loop={true}
loop={false}
autoPlay={isAutoPlay}
style={{ width: PAGE_WIDTH, height: 240 }}
width={PAGE_WIDTH}
data={[...new Array(6).keys()]}
renderItem={({ index, animationValue }) => {
return <CustomItem key={index} index={index} animationValue={animationValue} />;
Expand Down
Loading