# rytm-webflow

> rytm webflow pack - ASwap, ShowUp

Latest version **2.3.3** (published 2026-07-06) · UNLICENSED license · 0 weekly downloads

## Install

```sh
npm install rytm-webflow
pnpm add rytm-webflow
yarn add rytm-webflow
bun add rytm-webflow
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 2.3.3 |
| Published | 2026-07-06 |
| First published | 2019-10-20 |
| Weekly downloads | 0 |
| License | UNLICENSED |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 4 |
| Unpacked size | 110.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Rytm Digital / Roman Kühl |
| Maintainers | npm.rytm |

## Links

- npm: https://www.npmjs.com/package/rytm-webflow
- Repository: https://github.com/rytmdigital/rytm-webflow
- Homepage: https://github.com/rytmdigital/rytm-webflow.git
- Issues: https://github.com/rytmdigital/rytm-webflow/issues
- npm.io page: https://npm.io/package/rytm-webflow

## Dependencies (4)

- [gsap](https://npm.io/package/gsap.md) ^3.5.1
- [scrollmagic](https://npm.io/package/scrollmagic.md) ^2.0.7
- [imagesloaded](https://npm.io/package/imagesloaded.md) ^4.1.4
- [scrollmagic-plugin-gsap](https://npm.io/package/scrollmagic-plugin-gsap.md) ^1.0.4

## Recent versions

- 2.3.3 (latest) — 2026-07-06
- 2.3.2 — 2026-06-11
- 2.3.1 — 2026-04-09
- 2.3.0 — 2026-03-31
- 2.2.7 — 2026-03-28
- 2.2.6 — 2026-03-28
- 2.2.5 — 2026-03-12
- 2.2.4 — 2025-11-26
- 2.2.3 — 2024-11-25
- 2.2.2 — 2024-11-25
- 2.2.1 — 2024-11-22
- 2.2.0 — 2024-11-06
- 2.1.12 — 2024-11-05
- 2.1.11 — 2024-10-07
- 2.1.10 — 2024-09-27
- … 37 more at https://npm.io/package/rytm-webflow/versions

## README

# rytm-webflow
Rytm webflow pack is used to make websites rendered on the server (using a PHP or other back-end) more dynamic.  
A static website can be transformed into something we call a *pseudo single page application* (SPA). The pack includes  [ASwap](#aswap) - a framework under developement by Rytm.Digital since 2016 which uses *AJAX* / *fetch* to first load and then *swap* the page's content in user's browser.  
This package comes with a handy [WebflowView](#webflowview) and [WebflowListView](#webflowlistview).  
Another future rytm-webflow includes is [ShowUp](#showup) - a ScrollMagic.js wrapper which can be integrated with ASwap. 

  
**Install using NPM:**
```
$ npm install rytm-webflow --save
```
## Dependencies
- gsap
- imagesloaded
- scrollmagic
- scrollmagic-plugin-gsap

## <a name="aswap">ASwap</a>
### Basic usage example  
In your HTML for each request you neeed a wrapper (default wrapper selector: ```#stage```)  
Inside a wrapper you can have multiple view instances. Each view uses a name defined in ```data-as-view``` and an unique id defined in ```data-as-id```
```html
<div id="stage">
  <div data-as-view="item" data-as-id="uid">
    ...
  </div>
</div>
```

Init ASwap in your JS  
```js
import RytmWebflow from 'rytm-webflow'

RytmWebflow.aswap.init()
```

### More examples
#### Controllers
To programm some interaction within your ASwap views create a controller

```js
import RytmWebflow from 'rytm-webflow'

class ItemController extends RytmWebflow.Controller {
  addEventListeners() {
    // ...do your stuff
  }
  removeEventListeners() {
    // ...undo your stuff
  }
}
```

And pass the controller class to the ```routes``` parameter

```js
import RytmWebflow from 'rytm-webflow'
import ItemController from './ItemController'

RytmWebflow.aswap.init({
  routes: {
    "item" : {
      controller: ItemController
    }
  }
})
```
Read more about it -> [Controller class](#markdown-header-controller-class)

  
If you need your controller to **wait until all view's images are loaded** you can use the ```ControllerImgLoad``` class instance. This will trigger ```view.show()``` not before images are loaded. While images are being loaded an ```as-images-loading``` class name will be added to the view's container.  
Usage example:  
```js
import RytmWebflow from 'rytm-webflow'

class MyController extends RytmWebflow.ControllerImgLoad {
  loadImagesComplete() {
    super.loadImagesComplete();
    // ...do your stuff...
  }
}
```
If you are using the ```ControllerImgLoad``` - to hide the view before the show animation is triggered you can add some CSS, eg:
```css
.as-images-loading {
  display: none;
}
```


#### Views
If you need some fancy animations to show / hide your views you can create view instances.
```js
import RytmWebflow from 'rytm-webflow'

class ItemView extends RytmWebflow.View {
  show(container) {
    // ...do your GreenSock stuff 
  }
  hide(container) {
    // ...do your GreenSock stuff 
  }
}
```
Then include the ```ItemView``` class in the ```routes``` parameter

```js
import RytmWebflow from 'rytm-webflow'
import ItemController from './ItemController'
import ItemView from './ItemView'

RytmWebflow.aswap.init({
  routes: {
    "item" : {
      controller: ItemController,
      view: ItemView
    }
  }
})
```
Read more about it -> [View class](#markdown-header-view-class)
#### Sample params
```js
import RytmWebflow from 'rytm-webflow'
import { trace } from 'rytm-helpers'
import routes from './routes'

RytmWebflow.aswap.init({
  animationTimeHide: 900,
  animationTimeShow: 1200,
  preloadOnHover: true,
  routes,
  trace,
})
```


### Params

| param  | default  | info  |
|---|---|---|
| ```animationTimeHide```  | ```800``` | Time (ms) for view's *hide* transition|
| ```animationTimeShow```  | ```800``` | Time (ms) for view's *show* transition|
| ```formsEnabled```  | ```true``` | Use *ASwap* on forms|
| ```formsSelector```  | ```'form'``` | *ASwap* forms selector|
| ```swipeEnabled```  | ```true``` | Use *ASwap* [swipe](#swipe)|
| ```swipeMinDistance```  | ```5``` | Minimum swipe move distance (px) for [swipe](#swipe) to fire|
| ```historyEnabled``` | ```true``` | Use history push state |
| ```historyFallback``` | ```true``` | Fallback if browser dosn't support history push state |
| ```cacheOn``` | ```true``` | By default all requests are being cached. Set to ```false``` to turn cache off |
| ```noCacheClassName``` | ```'no-as-cache'``` | By default all requests are being cached. Links with this class name don't use ajax cache |
| ```resetScrollOn``` | ```true``` | By default on each view swap the window is scrolled back up. This should be disabled if using ```lenis``` |
| ```noScrollClassName``` | ```'no-as-scroll'``` | By default each page transformation is reseting the window scroll. Links with this class don't scroll window back up |
| ```noSwapClassName``` | ```'no-as'``` | Links with this class or ```target=_blank``` don't load using ASwap |
| ```oldschoolBrowserFallback``` | ```true``` | If set to ```true``` IE 11 or lower is not supported |
| ```preloadOnHover``` | ```false``` | If ```true``` each hover will run request - to make page work faster |
| ```refreshOnSameUrl``` | ```true``` | When ```true``` each request on same URL will refresh browser's window |
| ```routes``` | ```{}``` | The routes object where you can define controllers and views |
| ```swapSelector``` | ```'#stage'``` | ASwap wrapper selector |
| ```defaultDocumentScrollBehavior``` | ```'smooth'``` | Default document scroll behaviour |
| ```trace``` | ```(message) => {}``` | A function to log stuff, eg in console |

### Controller class

| method  | info  |
|---|---|
| ```addEventListeners()```  | this method is called after a view is shown on stage |
| ```removeEventListeners()```  | this method is called just before the view is being hidden |
| ```onRequestUrl()```  | read more -> [global events](#markdown-header-global-events) |
| ```onRequestLoad()```  | read more -> [global events](#markdown-header-global-events) |
| ```onRequestLoaded()```  | read more -> [global events](#markdown-header-global-events) |
| ```onViewHide()```  | read more -> [global events](#markdown-header-global-events) |
| ```onViewHidden()```  | read more -> [global events](#markdown-header-global-events) |
| ```onViewSwapStart()```  | read more -> [global events](#markdown-header-global-events) |
| ```onViewSwapComplete()```  | read more -> [global events](#markdown-header-global-events) |
| ```onViewShow()```  | read more -> [global events](#markdown-header-global-events) |
| ```onViewShown()```  | read more -> [global events](#markdown-header-global-events) |

### View class
| method  | parameters  | info  |
|---|---|---|
| ```hide()```  | ```container``` | this method is called by the ```Controller``` instance in the ```onViewShow()``` method |
| ```prepare()```  | ```container``` | this method is called by the ```Controller``` instance in the ```onViewSwapComplete()``` method |
| ```show()```  | ```container``` | this method is called by the ```Controller``` instance in the ```onViewShow()``` method |
| ```loadImagesStart()```  | ```container``` | this method is called by the ```ControllerImgLoad``` instance  on image load start |
| ```loadImagesComplete()```  | ```container``` | this method is called by the ```ControllerImgLoad``` instance  on image load complete |

### Global events
You also can pass custom methods to the global ASwap instance events, eg:
```js
import RytmWebflow from 'rytm-webflow'

RytmWebflow.aswap.init(params, {
  onRequestLoad: () => {  },
  onViewHidden: () => { }
})
```
| method  | info  |
|---|---|
| ```onInited()```  | fired when ASwap is inited |
| ```onRequestUrl()```  | fired when a new url is requested |
| ```onRequestLoad()```  | fired when a new url is loaded (not whired when url is already saved in cache) |
| ```onRequestLoaded()```  | fired when a url load has completed |
| ```onViewHide()```  | fired when a ASwap is starting it's hide transition. Hide is fired for all views which are available on current stage and are not available on new / loaded stage  |
| ```onViewHidden()```  | fired within ```animationTimeHide``` ms from hide transition start |
| ```onViewSwapStart()```  | fired just before the scene's content is replaced |
| ```onViewSwapComplete()```  | fired just after the scene's content is being replaced |
| ```onViewShow()```  | fired when the new scene is loaded and the show transition is being started |
| ```onViewShown()```  | fired within ```animationTimeShow``` ms from show transition start |

### Bootstrap integration ###
#### Bootstrap v5 ####
A common situation is to hide all Bootstrap offcanvases or modals when an ASwap page transition is in made. To do so just call the `bootstrap5.hideFlyovers()` method. You can also hide offcanvases or modals separatly by calling the `bootstrap5.hideOffcanvases()` or `bootstrap5.hideModals()` methods.
A basic usage example:
```js
import RytmWebflow from 'rytm-webflow'
import bootstrap5 from 'rytm-webflow/bootstrap'

RytmWebflow.aswap.init(params, {
  onViewHide: () => {
    bootstrap5.hideFlyovers()
  },
})
```
## <a name="swipe">Swipe</a> ##
Add urls to `as-swipe-{direction}` data attributes, eg:
```html
<div
	data-as-swipe-left="/path/to-next"
	data-as-swipe-right="/path/to-prev"
	data-as-swipe-up="/path/to-back">
	...
</div>
```

## <a name="webflowview">WebflowView</a> ##
WebflowView is an universal ASwap View which can be used with the **Webflow syntax** - a minified way to describe an animation, it's duration (time), easing, delay or the trigger. Eg. `y:100,o:0` will animate an element from: `y:100px` and `opacity: 0` to it's current state. Go to [Webflow syntax](#webflowsyntax) for more.
### Basic usage example  
Define the route:
```js
import RytmWebflow from 'rytm-webflow'

RytmWebflow.aswap.init({
  routes: {
    "webflow" : {
      controller: RytmWebflow.Controller,
      view: RytmWebflow.WebflowView
    }
  }
})
```
Add data attributes to the cointainer element:
```html
<div id="stage">
  <div data-as-view="webflow" data-as-id="uid">
    ...
  </div>
</div>
```
Animate any element within the *WebflowView* container using the **Webflow syntax** inside the `webview` attributes (initial, show, hide):
```html
<div data-webview-initial="o:0" data-webview-show="o:1,t:.5" data-webview-hide="o:0,t:.4">
  ...
</div>
```
or (more complex example):
```html
<div data-webview-initial="x:-10,y:50" data-webview-show="x:0,y:0,t:.5,d:.15,e:power3.out" data-webview-hide="x:10,y:-25,t:.4,d:.2,e:power2.in">
  ...
</div>
```
### Stagger - animate any list of items
Animate elements nested under the WebflowView container when scrolled into viewport by using `stagger` data attributes (initial, show, hide) and the `data-webset` attribute:
```html
<ul data-stagger-initial="o:0" data-stagger-show="o:1,t:.5,stagger:.1" data-stagger-hide="o:0,t:.4,stagger:.1" data-webset="selector:li">
  <li>...</li>
  <li>...</li>
</ul>
```
### Scrollmagic - animate elements when scrolled into viewport
Scroll-triggered-animations of any element inside the *WebflowView* container. Use the `webscroll` data attributes (initial, show, hide):
```html
<div data-webscroll-initial="o:0" data-webscroll-show="o:1,t:.5" data-webscroll-hide="o:0,t:.3">
  ...
</div>
```
Use the `webset` data attribute to pass some additional parameters, eg. the trigger element. This can be helpful when animating any element in the Y axis, eg:
```html
<div data-webset="trigger:parent" data-webscroll-initial="y:100" data-webscroll-show="y:0,t:.5,e:power3.out" data-webscroll-hide="o:0,t:.4">
  ...
</div>
```
By default scroll-troggered animations are only fired once. You can change this by setting the `fireOnce` parameter to `false`, example:
```html
<div data-webset="fireOnce:false" data-webscroll-initial="o:0" data-webscroll-show="o:1,t:.5">
  ...
</div>
```
## <a name="webflowlistview">WebflowListView</a> ##
The WebflowListView enables staggered *show* animation for elements which implement the WebflowView Scrollmagic animations. 
### Basic usage example  
Define the route:
```js
import RytmWebflow from 'rytm-webflow'

RytmWebflow.aswap.init({
  routes: {
    "list" : {
      controller: RytmWebflow.Controller,
      view: RytmWebflow.WebflowListView
    }
  }
})
```
Add data attributes to the cointainer element. Inside the element grabbed by the selector place elements that include the `webscroll` data attributes (initial, show, hide):
```html
<div id="stage">
  <ul data-as-view="list" data-as-id="uid" data-webset="selector:li,stagger:.1">
    <li>
      <div data-webscroll-initial="o:0" data-webscroll-show="o:1,t:.5" data-webscroll-hide="o:0,t.4">
        ...
      </div>
    </li>
  </ul>
</div>
```


## <a name="showup">ShowUp</a> ##
ShowUp makes creating gsap scroll-triggered-animations much faster. It can be used to show content while user scrolls down the page. It can also be used for parallax like animations. ShowUp comes with the **Webflow syntax**. Go to [Webflow syntax](#webflowsyntax) for more.
### Webflow usage example  
To enable ShowUp call the init method in your JS:
```js
import RytmWebflow from 'rytm-webflow'

RytmWebflow.showUp.init()
```
If you are working on a SPA or using ASwap the only thing you need to do is call the rebuild method on each view load. Check out the ASwap example:
```js
import RytmWebflow from 'rytm-webflow'

const events = {
  onViewSwapComplete: () => {
    RytmWebflow.showUp.rebuild()
  }
}
RytmWebflow.aswap.init({}, events)
```
To implement ShowUp on your HTML add a `data-webflow` or `data-webpara` attribute to DOM elements which will describe the animation
```html
<!-- From opacity: 0 in .4s -->
<div data-webflow="o:0,t:.4">...</div>
```
Add easing for your animations
```html
<!-- gsap easing -->
<div data-webflow="x:-10vw,t:.5,e:expo.out">...</div>
```
Delay animations
```html
<!-- delay by .3s -->
<div data-webflow="x:-20,t:.5,d:.3">...</div>
```
#### Trigger element
By default the ScrollMagic.Scene is triggered by the element itself. Also it is triggered once the element becomes visible (at the window bottom).
You can override these defaults using the `data-webset` attribute.  
To specyfy an offset:
```html
<!-- offset: 100px from bottom up -->
<div data-webflow="o:0" data-webset="offset:100">...</div>

<!-- offset: window center -->
<div data-webflow="o:0" data-webset="offset:center">...</div>

<!-- offset: window top -->
<div data-webflow="o:0" data-webset="offset:top">...</div>
```
To specyfy a trigger element:
```html
<!-- target -->
<div data-webflow="o:0" data-webset="trigger:#myTarget">...</div>
```
#### *Hide* animation
You can specify a *hide* transition (eg. for ASwap View to hide elements)
Separate *show* and *hide* animation using `;`
```html
<!-- show, hide -->
<div data-webflow="x:-30,t:.5;x:15,t:.3">...</div>
```
To start the hide animation on each dom element call the `hide()` method
```js
RytmWebflow.showUp.hide()
```
An ASwap example implementing ShowUp along with the *hide* animation:
```js
import RytmWebflow from 'rytm-webflow'

const events = {
  onViewSwapComplete: () => {
    RytmWebflow.showUp.rebuild()
  },
  onViewHide: () => {
    RytmWebflow.showUp.hide()
  }
}
RytmWebflow.aswap.init({}, events)
```
### Parallax usage example  
Parallax like animation:
```html
<!-- parallax duration 100% - by container height -->
<div data-webpara="x:100" data-webset="duration:100%">...</div>
```
Duration can be relative to viewport:
```html
<!-- parallax duration 30vh - by viewport height -->
<div data-webpara="s:1.2" data-webset="duration:30vh">...</div>
```
When moving elements in the Y axis you can use the `parent` trigger hook:
```html
<!-- parallax triggered by parent Node -->
<div>
  <div data-webpara="y:-100" data-webset="duration:100%,trigger:parent"></div>
</div>
```
### <a name="webflowsyntax">Webflow syntax</a> ##
| short code  | prameter  | example |
|---|---|---|
| ```d```  | delay | ```"d:.4"``` |
| ```e```  | ease | ```"e:Bounce.easeOut"``` |
| ```h```  | height | ```"h:10"``` |
| ```o```  | opacity | ```"o:0"``` |
| ```r```  | rotation | ```"r:180"``` |
| ```s```  | scale | ```"s:.1"``` |
| ```t```  | time | ```"t:.35"``` |
| ```w```  | width |  ```"w:1200"```| |

### More examples
Examples:
```html
<h1>Webflow show up</h1>
<div data-webflow="t:.4,o:0,y:10;t:.4,o:0,y:-30">Webflow Simple ALPHA no delay</div>
<div data-webflow="t:.4,o:0,s:.4;t:.4,o:0,s:1.5">Webflow Scale</div>
<div data-webflow="t:1.4,x:-300,e:expo.out;t:.4,d:0,x:-100,e:expo.in" data-webset="offset:100">Webflow Offset: 100, Ease EXPO</div>
<div data-webflow="t:1.4,x:-100vw;t:.4,d:0,x:-250" data-webset="offset:center">Webflow Offset: center</div>
<div data-webflow="t:1.4,x:-250;t:.4,d:0,x:-250" data-webset="offset:top">Webflow offset: TOP</div>
<div data-webflow="t:1.4,x:-250,w:10;t:.4,d:0,x:-250" data-webset="trigger:#boom">Webflow Triggered by next BLOCK</div>
<div id="boom" >Trigger</div>
<!-- ### Webflow Parallax ### -->
<h1>Webflow parallax</h1>
<div data-webpara="x:400" data-webset="duration:100%">Parallax duration 100%</div>
<div data-webpara="x:400,e:back.out" data-webset="duration:40vh">Parallax  duration 40vh, ease: Back</div>
<div >parent TRIGGER
  <div data-webpara="y:-500,r:180" data-webset="duration:100%,trigger:parent">Parallax Y</div>
</div>
```

You can use the parser functions to implement your own view related methods using the Webflow syntax:  

| function  | descr  |
|---|---|
| ```RytmWebflow.getWebflowProps(str)```  | converts a webflow syntax string into an webflow parameters object: ```{show: {tween: {}, time: X}, hide: {tween: {}, time: X}``` |
| ```RytmWebflow.getParallaxProps(str)```  | converts a webflow syntax string into an webflow parameters object: ```{parallax: {tween: {}}}``` |

---
_Source: https://npm.io/package/rytm-webflow · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
