Skip to navigation

Interaction examples

A complete create call for each trigger type. For every field and value, see Interaction schema.

Each example assumes the site has exactly one hero-btn class, and limits the interaction to the current page.

Click

Fade in on click
const page = await webflow.getCurrentPage();
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Click fade',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:click',
config: {control: 'play'},
target: {extensionKey: 'wf:class', value: 'hero-btn'},
},
],
timelines: [
{
name: 'Fade',
actions: [
{
id: 'click-fade',
name: 'Fade',
tt: 2,
timing: {duration: 0.4, ease: 2},
properties: {'wf:transform': {opacity: ['0%', '100%']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

A from-to fade that starts at 0% keeps the element hidden until the first click.

Hover

Two triggers, one for enter and one for leave. Each routes to its own timeline with assignedGroupId.

Fade in on enter, fade out on leave
const page = await webflow.getCurrentPage();
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Hover fade',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:hover',
config: {
control: 'play',
assignedGroupId: 'hover-in',
pluginConfig: {multiTimeline: false, eventMode: 'enter'},
},
target: {extensionKey: 'wf:class', value: 'hero-btn'},
},
{
extensionKey: 'wf:hover',
config: {
control: 'play',
assignedGroupId: 'hover-out',
pluginConfig: {multiTimeline: false, eventMode: 'leave'},
},
target: {extensionKey: 'wf:class', value: 'hero-btn'},
},
],
timelines: [
{
name: 'Enter',
groupId: 'hover-in',
actions: [
{
id: 'hover-in',
name: 'Fade in',
tt: 2,
timing: {duration: 0.3, ease: 2},
properties: {'wf:transform': {opacity: ['0%', '100%']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
{
name: 'Leave',
groupId: 'hover-out',
actions: [
{
id: 'hover-out',
name: 'Fade out',
tt: 2,
timing: {duration: 0.3, ease: 2},
properties: {'wf:transform': {opacity: ['100%', '0%']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

The same result with one trigger and two timeline roles:

One trigger with two roles
triggers: [
{
extensionKey: 'wf:hover',
config: {control: 'play', pluginConfig: {multiTimeline: true}},
target: {extensionKey: 'wf:class', value: 'hero-btn'},
},
],
timelines: [
{name: 'Enter', triggerMetadata: {role: 'mouseEnter'}, actions: [/* fade in */]},
{name: 'Leave', triggerMetadata: {role: 'mouseLeave'}, actions: [/* fade out */]},
],

Page load

Fade in on page load
const page = await webflow.getCurrentPage();
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Load fade',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:load',
config: {control: 'play'},
},
],
timelines: [
{
name: 'Fade',
actions: [
{
id: 'load-fade',
name: 'Fade',
tt: 2,
timing: {duration: 0.6, ease: 2},
properties: {'wf:transform': {opacity: ['0%', '100%']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

Scroll

Play once as the element enters the viewport. The from tween (tt: 1) starts the element hidden.

Reveal once on scroll
const page = await webflow.getCurrentPage();
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Scroll reveal',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:scroll',
config: {
scrollTriggerConfig: {
start: 'top 90%',
end: 'bottom 15%',
enter: 'play',
leaveBack: 'reset',
},
},
target: {extensionKey: 'wf:class', value: 'hero-btn'},
},
],
timelines: [
{
name: 'Reveal',
actions: [
{
id: 'scroll-reveal',
name: 'Reveal',
tt: 1,
timing: {duration: 0.6, ease: 2},
properties: {'wf:transform': {opacity: ['0%']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

Scrub with the scroll position. A scrub needs canvasDuration: 1 on the timeline and duration: 1 on the action.

Scrub a fade with the scroll position
const page = await webflow.getCurrentPage();
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Scroll scrub fade',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:scroll',
config: {
scrollTriggerConfig: {
start: 'top bottom',
end: 'top 10%',
scrub: 0.3,
},
},
target: {extensionKey: 'wf:class', value: 'hero-btn'},
},
],
timelines: [
{
name: 'Scrub',
canvasDuration: 1,
actions: [
{
id: 'scroll-scrub',
name: 'Fade',
tt: 2,
timing: {duration: 1, ease: 2},
properties: {
'wf:transform': {opacity: ['0%', '100%'], xPercent: ['-40%', '0%']},
},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

Mouse move

wf:viewport tracks the cursor across the whole page. Each timeline has its own role.

Move an element with the cursor
const page = await webflow.getCurrentPage();
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Mouse follow',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:mouse-move',
config: {},
target: {extensionKey: 'wf:viewport', value: ''},
},
],
timelines: [
{
name: 'Mouse X',
triggerMetadata: {role: 'mouseX'},
actions: [
{
id: 'mouse-x',
name: 'Follow X',
tt: 2,
timing: {duration: 0.4},
properties: {'wf:transform': {x: ['0px', '40px']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
{
name: 'Mouse Y',
triggerMetadata: {role: 'mouseY'},
actions: [
{
id: 'mouse-y',
name: 'Follow Y',
tt: 2,
timing: {duration: 0.4},
properties: {'wf:transform': {y: ['0px', '40px']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

Custom event

Play on a custom event
const page = await webflow.getCurrentPage();
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Custom event flash',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:custom',
config: {
control: 'play',
pluginConfig: {eventName: 'my-event'},
},
target: {extensionKey: 'wf:body', value: ''},
},
],
timelines: [
{
name: 'Flash',
actions: [
{
id: 'custom-flash',
name: 'Flash',
tt: 2,
timing: {duration: 0.4, ease: 2},
properties: {'wf:transform': {opacity: ['100%', '20%']}},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

Fire the event from code on the published page, after interactions load. A DOM event does not play it.

Fire the event
const interactionsApi = Webflow.require('ix3');
interactionsApi.emit('my-event');

Class change

A Set action (tt: 3) that adds a class. selectors takes style block ids, not class slugs.

Add a class on click
const page = await webflow.getCurrentPage();
const style = await webflow.getStyleByName('is-active');
if (!style) throw new Error('Create the is-active class first.');
const created = await webflow.interactions.create({
pageId: page.id,
name: 'Add class on click',
scope: {type: 'pages', value: [page.id]},
triggers: [
{
extensionKey: 'wf:click',
config: {control: 'play'},
target: {extensionKey: 'wf:class', value: 'hero-btn'},
},
],
timelines: [
{
actions: [
{
id: 'add-class',
name: 'Add class',
tt: 3,
timing: {},
properties: {
'wf:class': {class: {operation: 'addClass', selectors: [style.id]}},
},
targets: [{extensionKey: 'wf:class', value: 'hero-btn'}],
},
],
},
],
});

When a saved interaction does not play

  • A custom event trigger has no eventName, or the page dispatches a DOM event instead of calling emit.
  • The action has hidden: true.
  • A from-to fade starts at 0%, so the element stays hidden until the trigger fires. This is expected.