This is an attempt to bring a Motion (formerly known as Framer Motion) to Svelte.
npm install motion-sv
Then import the motion
component:
<script lang="ts">
import { motion } from "motion-sv";
</script>
<motion.div animate={{ x: 100 }}>Hello</motion.div>
Some Framer Motion features cannot be reproduced in Svelte with the same APIs.
React uses getSnapshotBeforeUpdate
and Vue uses onBeforeUpdate
.
Svelte has no equivalent, so layout animations require a helper.
Enable them by wrapping motion
with createLayoutMotion
:
import { motion, createLayoutMotion } from "motion-sv";
const layout = createLayoutMotion(motion);
createLayoutMotion
provides:
layout.update()
— mark a layout change after state updates.layout.update.with(fn)
— wrap a state update so layout changes are tracked automatically.Usage:
<script lang="ts">
import { motion, createLayoutMotion } from "motion-sv";
let isOn = $state(false);
const layout = createLayoutMotion(motion);
const toggle = layout.update.with(() => (isOn = !isOn));
// or:
// function toggle() {
// isOn = !isOn;
// layout.update();
// }
</script>
<motion.button style={{ ...container, justifyContent: "flex-" + (isOn ? "start" : "end") }} onclick={toggle}>
<layout.div
style={handle}
layoutDependency={isOn}
transition={{ type: "spring", visualDuration: 0.2, bounce: 0.2 }}
/>
</motion.button>
Use
layoutDependency
if the element is not being remounted. This gives motion a reactive trigger for the update.
layoutId
Alternatively, use layoutId
to link elements across renders:
<motion.button style={{ ...container, justifyContent: "flex-" + (isOn ? "start" : "end") }} onclick={toggle}>
{#if isOn}
<layout.div
style={handle}
layoutId="handle"
transition={{
type: "spring",
visualDuration: 0.2,
bounce: 0.2,
}}
/>
{:else}
<layout.div
style={handle}
layoutId="handle"
transition={{
type: "spring",
visualDuration: 0.2,
bounce: 0.2,
}}
/>
{/if}
</motion.button>
Transition
/ TransitionGroup
.Limitations in this port:
AnimatePresence
(e.g. when
) may not work.