Skip to main content
Version: Next

withTiming

The withTiming modifier creates precise, time-based animations with exact durations. Unlike withSpring which uses physics, withTiming gives you complete control over how long an animation takes and how it accelerates/decelerates.

Why Use withTiming?

Timing animations are perfect when you need:

  • Exact duration - Know exactly how long an animation takes
  • Predictable timing - Coordinate multiple animations
  • Smooth easing - Control acceleration and deceleration
  • Precise control - Perfect for UI transitions, tooltips, dropdowns

Basic Syntax

withTiming(toValue, options?)

Parameters

  • toValue (required): The target value. Can be a number, string, object, or array.
  • options (optional): Configuration object for timing control.

Simple Example

The simplest timing animation:

import { useValue, withTiming, animate } from 'react-ui-animate';

function MyComponent() {
const [opacity, setOpacity] = useValue(0);

return (
<>
<button onClick={() => setOpacity(withTiming(1, { duration: 300 }))}>
Fade In
</button>
<animate.div
style={{
opacity,
width: 100,
height: 100,
background: 'teal',
}}
/>
</>
);
}

Configuration Options

OptionTypeDefaultDescription
durationnumber300Animation duration in milliseconds
easingfunctionLinearEasing function for acceleration/deceleration
onStart() => void-Called when animation starts
onChange(value) => void-Called on every frame with current value
onComplete() => void-Called when animation finishes

Real-World Examples

Example 1: Fade In/Out

Perfect for tooltips, dropdowns, and modals:

function FadeComponent() {
const [opacity, setOpacity] = useValue(0);

const show = () => {
setOpacity(withTiming(1, { duration: 300 }));
};

const hide = () => {
setOpacity(withTiming(0, { duration: 200 }));
};

return (
<>
<button onClick={show}>Show</button>
<button onClick={hide}>Hide</button>
<animate.div
style={{
opacity,
width: 100,
height: 100,
background: 'teal',
}}
/>
</>
);
}

Example 2: Slide Animation

Smooth slide transitions:

function SlidePanel() {
const [translateX, setTranslateX] = useValue(-200);

const open = () => {
setTranslateX(withTiming(0, { duration: 400 }));
};

const close = () => {
setTranslateX(withTiming(-200, { duration: 300 }));
};

return (
<>
<button onClick={open}>Open</button>
<button onClick={close}>Close</button>
<animate.div
style={{
translateX,
width: 200,
height: '100vh',
background: 'white',
boxShadow: '2px 0 8px rgba(0,0,0,0.1)',
}}
>
Panel Content
</animate.div>
</>
);
}

Example 3: Progress Bar

Animate progress with exact timing:

function ProgressBar() {
const [width, setWidth] = useValue(0);

const start = () => {
setWidth(withTiming(100, { duration: 2000 }));
};

return (
<>
<button onClick={start}>Start Progress</button>
<div style={{ width: 300, height: 8, background: '#e1e1e1', borderRadius: 4 }}>
<animate.div
style={{
width: `${width}%`,
height: '100%',
background: 'teal',
borderRadius: 4,
}}
/>
</div>
</>
);
}
import React from 'react';
import { useValue, animate, withTiming } from 'react-ui-animate';
import '../../styles.css';

export default function BasicExample() {
  const [obj, setObj] = useValue({ x: 0, y: 0, width: 100, height: 100 });

  return (
    <div className="container">
      <button
        className="button buttonPrimary"
        onClick={() =>
          setObj(
            withTiming(
              { x: 0, y: 0, width: 300, height: 100 },
              {
                duration: 400,
                onStart: () => console.log('START'),
                onComplete: () => console.log('Animation complete'),
              }
            )
          )
        }
      >
        Start
      </button>
      <button
        className="button buttonSecondary"
        onClick={() => setObj({ x: 0, y: 0, width: 100, height: 100 })}
      >
        Reset
      </button>

      <animate.div
        style={{
          width: obj.width,
          height: 100,
          backgroundColor: 'teal',
          margin: '20px auto 0',
          translateX: obj.x,
          translateY: obj.y,
          borderRadius: 8,
        }}
      />
    </div>
  );
}

Easing Functions

Control how the animation accelerates and decelerates:

// Linear - constant speed
setX(withTiming(100, { duration: 300 }));

// Ease in - slow start, fast end
setX(withTiming(100, {
duration: 300,
easing: (t) => t * t
}));

// Ease out - fast start, slow end
setX(withTiming(100, {
duration: 300,
easing: (t) => t * (2 - t)
}));

// Ease in-out - slow start and end
setX(withTiming(100, {
duration: 300,
easing: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
}));

Animating Multiple Values

Timing works great with objects and arrays:

const [style, setStyle] = useValue({ 
opacity: 0,
translateY: 50,
scale: 0.9
});

// Animate all properties together
setStyle(withTiming({
opacity: 1,
translateY: 0,
scale: 1
}, { duration: 400 }));

Using Callbacks

Track animation progress and trigger side effects:

function AnimatedCard() {
const [opacity, setOpacity] = useValue(0);

const fadeIn = () => {
setOpacity(withTiming(1, {
duration: 500,
onStart: () => {
console.log('Fade in started');
// Show loading indicator, disable buttons, etc.
},
onChange: (value) => {
// Track progress
console.log(`Opacity: ${(value * 100).toFixed(0)}%`);
},
onComplete: () => {
console.log('Fade in complete');
// Hide loading indicator, enable buttons, etc.
},
}));
};

return (
<>
<button onClick={fadeIn}>Fade In</button>
<animate.div
style={{
opacity,
width: 100,
height: 100,
background: 'teal',
}}
/>
</>
);
}

Common Patterns

Pattern 1: Quick Fade

const [opacity, setOpacity] = useValue(0);

const show = () => {
setOpacity(withTiming(1, { duration: 200 }));
};

const hide = () => {
setOpacity(withTiming(0, { duration: 150 }));
};

Pattern 2: Coordinated Animations

const [opacity, setOpacity] = useValue(0);
const [translateY, setTranslateY] = useValue(50);

// Animate both with same duration for coordination
const reveal = () => {
setOpacity(withTiming(1, { duration: 400 }));
setTranslateY(withTiming(0, { duration: 400 }));
};

Pattern 3: Staggered Timing

const [items, setItems] = useValue([0, 0, 0, 0]);

const reveal = () => {
// Each item fades in 100ms after the previous
items.forEach((_, i) => {
setTimeout(() => {
const newItems = [...items];
newItems[i] = 1;
setItems(newItems);
}, i * 100);
});
};

When to Use withTiming vs withSpring

Use CaseRecommended ModifierWhy
TooltipswithTimingQuick, predictable timing
DropdownswithTimingSnappy, controlled
Progress barswithTimingExact duration needed
ModalswithSpringNatural, bouncy feel
ButtonswithSpringResponsive, alive
CardswithSpringOrganic motion

Best Practices

✅ Do

  • Use withTiming when you need exact duration
  • Use consistent durations across related animations
  • Use easing functions for natural acceleration/deceleration
  • Leverage callbacks for coordination and side effects

❌ Don't

  • Don't use withTiming when you want natural, bouncy motion (use withSpring)
  • Don't make animations too slow (>1000ms) - users expect quick feedback
  • Don't forget to handle the animation lifecycle with callbacks
  • Don't animate layout properties - use transforms instead

Performance Tips

  1. Timing is optimized - Very performant for simple animations
  2. Use transforms - translateX, translateY, scale are GPU-accelerated
  3. Batch updates - Animate multiple properties in one object
  4. Keep durations reasonable - 200-500ms is usually ideal

Troubleshooting

Animation feels too fast:

  • Increase duration (try 400-600ms)

Animation feels too slow:

  • Decrease duration (try 150-250ms)

Animation feels mechanical:

  • Add easing function for natural acceleration/deceleration
  • Or consider using withSpring for more natural motion

Next Steps