@pankajtime12/react-native-squircle-view
v0.1.31
Published
Figma-flavored squircles for React Native with gradient support — no native modules
Maintainers
Readme
What is a Squircle?
A squircle is the smooth-corner shape used throughout Apple's iOS icons, Figma, and modern design systems. Unlike borderRadius which creates a simple circular arc, squircles use a mathematically precise superellipse curve that blends naturally into straight edges — giving a much more premium, fluid appearance.
| Standard borderRadius | Squircle (cornerSmoothing: 1) |
|---|---|
| Abrupt arc → straight line | Smooth continuous curve |
Features
- ✅ Figma-identical squircle shapes — powered by
figma-squircle - 🎨 Gradient fills — linear (horizontal, vertical, diagonal) and radial
- 🖊️ Stroke support — inset stroke that stays inside the shape
- 🔲 Per-corner radii — set each corner independently
- 🎬 Reanimated animations — animate radius, smoothing, color, and stroke with
SharedValue - 🏗️ Layout animations — full support for
entering,exiting,layoutprops - ⚡ No native modules — pure JS/SVG, works with Expo Go
- 🔑 TypeScript-first — fully typed API
Installation
npm install @pankajtime12/react-native-squircle-view react-native-svg figma-squircle
# or
yarn add @pankajtime12/react-native-squircle-view react-native-svg figma-squircleNote:
react-native-svgis a required peer dependency. Follow its installation guide if you haven't already.
Optional: Animated support
To use <AnimatedSquircleView>, also install Reanimated:
npm install react-native-reanimatedFollow the Reanimated installation guide.
Quick Start
import { SquircleView } from '@pankajtime12/react-native-squircle-view';
export default function App() {
return (
<SquircleView
squircleParams={{
cornerRadius: 24,
cornerSmoothing: 0.6,
fillColor: '#6366f1',
}}
style={{ width: 200, height: 100 }}
/>
);
}Usage
Solid Fill
import { SquircleView } from '@pankajtime12/react-native-squircle-view';
<SquircleView
squircleParams={{
cornerRadius: 36,
cornerSmoothing: 1, // 0 = standard border-radius, 1 = fully Figma-smooth
fillColor: '#56CDF2',
}}
style={{ width: '100%', aspectRatio: 2 }}
/>Linear Gradient
<SquircleView
squircleParams={{
cornerRadius: 36,
cornerSmoothing: 1,
gradient: {
type: 'linear',
x1: '0%', y1: '0%', // start point
x2: '100%', y2: '0%', // end point (horizontal)
stops: [
{ offset: '0%', color: '#6366f1' },
{ offset: '100%', color: '#ec4899' },
],
},
}}
style={{ width: '100%', aspectRatio: 2 }}
/>Diagonal gradient:
gradient: {
type: 'linear',
x1: '0%', y1: '0%',
x2: '100%', y2: '100%',
stops: [
{ offset: '0%', color: '#f59e0b' },
{ offset: '50%', color: '#ef4444' },
{ offset: '100%', color: '#8b5cf6' },
],
},Radial Gradient
<SquircleView
squircleParams={{
cornerRadius: 36,
cornerSmoothing: 1,
gradient: {
type: 'radial',
cx: '50%', cy: '50%', // center
r: '60%', // radius
stops: [
{ offset: '0%', color: '#34d399' },
{ offset: '100%', color: '#065f46' },
],
},
}}
style={{ width: '100%', aspectRatio: 2 }}
/>Stroke
<SquircleView
squircleParams={{
cornerRadius: 36,
cornerSmoothing: 1,
fillColor: '#1e1b4b',
strokeColor: '#ffffff',
strokeWidth: 3, // always rendered inside the shape
}}
style={{ width: '100%', aspectRatio: 2 }}
/>Per-Corner Radii
Each corner can have an independent radius. Omit cornerRadius and set individual corners:
<SquircleView
squircleParams={{
topLeftCornerRadius: 48,
topRightCornerRadius: 12,
bottomRightCornerRadius: 48,
bottomLeftCornerRadius: 12,
cornerSmoothing: 0.8,
fillColor: '#f97316',
}}
style={{ width: '100%', aspectRatio: 2 }}
/>With Children (e.g. a Button)
<SquircleView> is a drop-in replacement for <View>. Children are rendered on top of the squircle background:
<SquircleView
squircleParams={{
cornerRadius: 18,
cornerSmoothing: 1,
gradient: {
type: 'linear',
x1: '0%', y1: '0%', x2: '100%', y2: '0%',
stops: [
{ offset: '0%', color: '#6366f1' },
{ offset: '100%', color: '#8b5cf6' },
],
},
}}
style={{ paddingVertical: 20, alignItems: 'center' }}
>
<Text style={{ color: '#fff', fontWeight: '700', fontSize: 16 }}>
Gradient Button
</Text>
</SquircleView>Animated API
Requires
react-native-reanimated ≥ 3.0.0
Use <AnimatedSquircleView> to animate any numeric squircle parameter with a SharedValue. The shape updates every frame — no JS-thread involvement after mount.
Tap to Morph Corners
import { AnimatedSquircleView } from '@pankajtime12/react-native-squircle-view';
import { useSharedValue, withSpring } from 'react-native-reanimated';
import { Pressable } from 'react-native';
function MorphButton() {
const cornerRadius = useSharedValue(8);
const cornerSmoothing = useSharedValue(0.1);
const toggle = () => {
cornerRadius.value = withSpring(56, { damping: 12, stiffness: 100 });
cornerSmoothing.value = withSpring(1, { damping: 14 });
};
return (
<Pressable onPress={toggle}>
<AnimatedSquircleView
animatedSquircleParams={{
cornerRadius, // ← pass SharedValue directly
cornerSmoothing, // ← pass SharedValue directly
fillColor: '#6366f1',
}}
style={{ width: 200, height: 100 }}
/>
</Pressable>
);
}Looping Pulse
import { withRepeat, withSequence, withTiming, Easing } from 'react-native-reanimated';
function PulseDemo() {
const cornerSmoothing = useSharedValue(0);
useEffect(() => {
cornerSmoothing.value = withRepeat(
withSequence(
withTiming(1, { duration: 1200, easing: Easing.inOut(Easing.quad) }),
withTiming(0, { duration: 1200, easing: Easing.inOut(Easing.quad) }),
),
-1, // infinite
);
}, []);
return (
<AnimatedSquircleView
animatedSquircleParams={{
cornerRadius: 32,
cornerSmoothing, // ← animates 0 → 1 → 0 in a loop
fillColor: '#0ea5e9',
}}
style={{ width: 200, height: 100 }}
/>
);
}Animated Stroke
function StrokeBreath() {
const strokeWidth = useSharedValue(0);
const cornerRadius = useSharedValue(20);
useEffect(() => {
strokeWidth.value = withRepeat(
withSequence(
withTiming(12, { duration: 900 }),
withTiming(1, { duration: 900 }),
),
-1,
);
}, []);
return (
<AnimatedSquircleView
animatedSquircleParams={{
cornerRadius: 32,
cornerSmoothing: 0.8,
fillColor: '#1e1b4b',
strokeColor: '#818cf8',
strokeWidth, // ← animates stroke width
}}
style={{ width: 200, height: 100 }}
/>
);
}Color Interpolation with interpolateColor
fillColor and strokeColor accept a SharedValue<string>, so you can smoothly cycle between any colors using Reanimated's interpolateColor + useDerivedValue — all on the UI thread:
import {
AnimatedSquircleView,
} from '@pankajtime12/react-native-squircle-view';
import {
useSharedValue,
useDerivedValue,
withRepeat,
withTiming,
interpolateColor,
Easing,
} from 'react-native-reanimated';
import { useEffect } from 'react';
function ColorCycleDemo() {
// A 0 → 1 progress driver that loops forever
const progress = useSharedValue(0);
useEffect(() => {
progress.value = withRepeat(
withTiming(1, { duration: 2000, easing: Easing.inOut(Easing.quad) }),
-1, // infinite
true, // reverse (ping-pong: 0 → 1 → 0 → 1 …)
);
}, []);
// Derive an animated color string from the progress value
const fillColor = useDerivedValue(() =>
interpolateColor(
progress.value,
[0, 1],
['#6366f1', '#ec4899'], // purple → pink
)
);
const strokeColor = useDerivedValue(() =>
interpolateColor(
progress.value,
[0, 1],
['#818cf8', '#f9a8d4'], // light purple → light pink
)
);
return (
<AnimatedSquircleView
animatedSquircleParams={{
cornerRadius: 32,
cornerSmoothing: 1,
fillColor, // ← SharedValue<string>, updates every frame on UI thread
strokeColor, // ← SharedValue<string>
strokeWidth: 3,
}}
style={{ width: '100%', aspectRatio: 2 }}
/>
);
}Cycling through multiple colors — pass more input/output stops to interpolateColor:
const fillColor = useDerivedValue(() =>
interpolateColor(
progress.value,
[0, 0.33, 0.66, 1],
['#6366f1', '#ec4899', '#f59e0b', '#10b981'], // purple → pink → amber → green
)
);Note:
gradientstops are intentionally non-animatable (react-native-svg limitation). UsefillColor+interpolateColorfor smooth color animation, or swap between named gradient presets using React state.
Layout Animations
<AnimatedSquircleView> accepts entering, exiting, and layout props from Reanimated — exactly like <Animated.View>:
import { FadeInDown, ZoomOut, LinearTransition } from 'react-native-reanimated';
<AnimatedSquircleView
entering={FadeInDown.duration(400).springify()}
exiting={ZoomOut.duration(300)}
layout={LinearTransition.springify()}
animatedSquircleParams={{
cornerRadius: 20,
cornerSmoothing: 0.8,
fillColor: '#6366f1',
}}
style={{ width: '100%', height: 64 }}
>
<Text style={{ color: '#fff' }}>I animate in and out!</Text>
</AnimatedSquircleView>Advanced: Custom SVG Worklet
For advanced use-cases where you need the squircle SVG path inside your own useAnimatedProps:
import { getSvgPathWorklet } from '@pankajtime12/react-native-squircle-view';
import { useAnimatedProps, useSharedValue } from 'react-native-reanimated';
import { Path } from 'react-native-svg';
import AnimatedPath from 'react-native-svg/src/elements/Path'; // re-export from your app
function MyCustomShape() {
const cornerRadius = useSharedValue(16);
const animatedProps = useAnimatedProps(() => {
'worklet';
return {
d: getSvgPathWorklet({
width: 200,
height: 100,
cornerRadius: cornerRadius.value,
cornerSmoothing: 0.6,
}),
};
});
return <AnimatedPath animatedProps={animatedProps} fill="#6366f1" />;
}API Reference
<SquircleView>
A drop-in replacement for React Native's <View>. Accepts all standard ViewProps plus:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| squircleParams | SquircleParams | ✅ | Shape, fill, gradient, and stroke configuration |
<AnimatedSquircleView>
Like <SquircleView> but accepts SharedValue<number> for any numeric field. Also accepts Reanimated's entering, exiting, layout, and style (with useAnimatedStyle).
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| animatedSquircleParams | AnimatedSquircleParams | ✅ | Shape params where numeric fields can be SharedValue<number> |
SquircleParams
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| cornerRadius | number | 0 | Uniform corner radius for all four corners |
| topLeftCornerRadius | number | — | Overrides top-left corner (takes priority over cornerRadius) |
| topRightCornerRadius | number | — | Overrides top-right corner |
| bottomRightCornerRadius | number | — | Overrides bottom-right corner |
| bottomLeftCornerRadius | number | — | Overrides bottom-left corner |
| cornerSmoothing | number | required | 0 = standard arc, 1 = fully Figma-smooth superellipse |
| fillColor | ColorValue | undefined | Solid fill color. Ignored when gradient is set |
| gradient | GradientParams | undefined | Gradient fill — takes priority over fillColor |
| strokeColor | ColorValue | undefined | Stroke color |
| strokeWidth | number | 0 | Stroke width (always inset — stays inside the shape) |
GradientParams
Linear Gradient
{
type: 'linear';
x1?: string | number; // start x, default '0%'
y1?: string | number; // start y, default '0%'
x2?: string | number; // end x, default '100%'
y2?: string | number; // end y, default '0%'
stops: GradientStop[];
}Radial Gradient
{
type: 'radial';
cx?: string | number; // center x, default '50%'
cy?: string | number; // center y, default '50%'
r?: string | number; // radius, default '50%'
fx?: string | number; // focal x (defaults to cx)
fy?: string | number; // focal y (defaults to cy)
stops: GradientStop[];
}GradientStop
{
offset: string | number; // e.g. '0%', '50%', 1.0
color: string; // any CSS color string
opacity?: number; // 0–1, default 1
}AnimatedSquircleParams
Same as SquircleParams but every numeric field also accepts SharedValue<number>, and fillColor / strokeColor accept SharedValue<string>.
Note:
gradientis intentionally non-animated —react-native-svgdoes not support animating<Stop>props viauseAnimatedProps.
getSvgPathWorklet(params)
A worklet-compatible function to compute the SVG path string on the UI thread:
getSvgPathWorklet({
width: number;
height: number;
cornerRadius?: number;
topLeftCornerRadius?: number;
topRightCornerRadius?: number;
bottomRightCornerRadius?: number;
bottomLeftCornerRadius?: number;
cornerSmoothing: number;
preserveSmoothing?: boolean;
}): stringPeer Dependencies
| Package | Version | Required |
|---------|---------|----------|
| react | * | ✅ |
| react-native | * | ✅ |
| react-native-svg | * | ✅ |
| react-native-reanimated | ≥ 3.0.0 | ⚡ Only for AnimatedSquircleView |
Contributing
Contributions are welcome! See CONTRIBUTING.md for development setup and guidelines.
License
MIT — see LICENSE.
