GSAP in WordPress gives you precise control over animations, transitions, scroll effects and complex motion without requiring WordPress-specific animation APIs.
GSAP, short for GreenSock Animation Platform, is a JavaScript animation library. It can animate CSS properties, SVG elements, JavaScript values and many other properties while providing tools for sequencing, easing, scroll-driven effects and complex interactive interfaces.
WordPress does not change the way GSAP itself works. The important part is integrating the library correctly into WordPress’s script-loading system.
A solid setup looks like this:
WordPress
↓
enqueue GSAP Core
↓
enqueue optional GSAP plugins
↓
enqueue your animation script
↓
wait for the required DOM
↓
create animations
This guide covers the complete beginner workflow: what GSAP is, how to load it correctly in WordPress, how dependencies work, how to write your first tween and timeline, how to add ScrollTrigger, how to avoid common loading problems and how to keep animations performant and accessible.
What is GSAP?
GSAP is a JavaScript animation platform designed for programmatic motion on the web.
Instead of describing an animation entirely through CSS:
.box {
transition: transform 1s ease;
}
you can control it through JavaScript:
gsap.to(".box", {
x: 100,
duration: 1
});
GSAP calculates and applies the intermediate property values required to move the element from its current state to the requested final state.
What can GSAP animate?
Common targets include:
- position;
- opacity;
- scale;
- rotation;
- colors;
- CSS transforms;
- SVG properties;
- numeric JavaScript values;
- scroll-driven sequences;
- complex groups of elements.
For example:
gsap.to(".card", {
x: 120,
opacity: 0.5,
rotation: 15,
duration: 1
});
can animate several properties at the same time.
GSAP is not a WordPress plugin
This distinction is useful.
GSAP itself is a JavaScript library:
gsap.min.js
WordPress is responsible for loading that JavaScript into the page.
After it is loaded, your animation code uses the normal GSAP API.
GSAP’s official installation documentation describes GSAP as framework-agnostic, meaning it can be used with WordPress, React and other web environments.
Understanding GSAP Core and plugins
GSAP is built around a core library plus optional plugins.
GSAP Core
The main library provides functionality such as:
gsap.to();gsap.from();gsap.fromTo();gsap.timeline();- easing;
- staggered animations;
- callbacks;
- animation controls.
For many animations, Core is all you need.
GSAP plugins
Plugins extend GSAP with additional capabilities.
Examples include:
- ScrollTrigger;
- ScrollSmoother;
- SplitText;
- Flip;
- Draggable;
- MotionPathPlugin;
- DrawSVGPlugin;
- MorphSVGPlugin.
One of the most commonly used plugins on WordPress websites is ScrollTrigger, which connects animations to scroll position.
GSAP 3.13 and newer
Current GSAP distribution is simpler than many older tutorials suggest. GSAP’s current installation documentation states that the private npm repository is no longer maintained and that GSAP and its plugins are available through the normal npm package from version 3.13 onward.
That means modern projects can simply use:
npm install gsap
when working with a build system.
The correct way to load GSAP in WordPress
WordPress has its own dependency-aware script system.
The recommended frontend hook is:
wp_enqueue_scripts
and the main function for loading JavaScript is:
wp_enqueue_script()
The official WordPress wp_enqueue_script() documentation describes this as the recommended method for adding JavaScript to a WordPress-generated page.
If you want a deeper explanation of WordPress’s loading system before continuing, see wp_enqueue_scripts explained.
Basic local GSAP setup
Suppose your theme contains:
/assets/js/gsap.min.js
/assets/js/app.js
You could enqueue them like this:
function mytheme_enqueue_animations() {
wp_enqueue_script(
'mytheme-gsap',
get_theme_file_uri( '/assets/js/gsap.min.js' ),
array(),
'3.13.0',
array(
'in_footer' => true,
)
);
wp_enqueue_script(
'mytheme-animations',
get_theme_file_uri( '/assets/js/app.js' ),
array( 'mytheme-gsap' ),
'1.0.0',
array(
'in_footer' => true,
)
);
}
add_action(
'wp_enqueue_scripts',
'mytheme_enqueue_animations'
);
The important part is:
array( 'mytheme-gsap' )
on the animation file.
That tells WordPress:
app.js
depends on
gsap.min.js
WordPress can therefore preserve the correct loading order.
Why dependencies matter
Your custom animation file may contain:
gsap.to(".hero", {
opacity: 1
});
But the global:
gsap
object does not exist until GSAP Core has executed.
If the browser receives:
app.js
↓
GSAP Core
instead of:
GSAP Core
↓
app.js
you may receive:
ReferenceError:
gsap is not defined
Do not manage the order manually when WordPress can manage it
This:
array( 'mytheme-gsap' )
is safer than relying only on the order in which several unrelated enqueue functions happen to run.
WordPress maintains a dependency tree and loads registered dependencies before scripts that require them.
Loading GSAP from a CDN
You do not have to keep GSAP locally.
A CDN URL can also be passed to wp_enqueue_script().
Conceptually:
function mytheme_enqueue_animations() {
wp_enqueue_script(
'mytheme-gsap',
'https://cdn.example.com/gsap.min.js',
array(),
'3.13.0',
array(
'in_footer' => true,
)
);
wp_enqueue_script(
'mytheme-animations',
get_theme_file_uri( '/assets/js/app.js' ),
array( 'mytheme-gsap' ),
'1.0.0',
array(
'in_footer' => true,
)
);
}
add_action(
'wp_enqueue_scripts',
'mytheme_enqueue_animations'
);
The WordPress dependency model remains the same.
CDN or self-hosted?
Both approaches can work.
A CDN may provide:
- easy setup;
- edge delivery;
- simple version selection.
A local copy provides:
- greater deployment control;
- fewer public third-party requests;
- control over the exact file being served;
- less dependency on a separate public CDN.
The dedicated comparison is covered in CDN vs. self-hosted assets in WordPress.
Your first GSAP animation in WordPress
Once GSAP is loaded, create an element to animate.
For example:
<div class="gsap-box">
Animate me
</div>
Your CSS might contain:
.gsap-box {
width: 120px;
height: 120px;
}
Then inside:
app.js
you can write:
gsap.to(".gsap-box", {
x: 200,
duration: 1
});
The element moves 200 pixels along the X axis over one second.
Understanding the syntax
The first argument:
".gsap-box"
is the target.
The second argument:
{
x: 200,
duration: 1
}
is the configuration object.
It describes what should happen.
The basic mental model
gsap.to(
target,
properties
)
For example:
gsap.to(".logo", {
scale: 1.2,
rotation: 10,
opacity: 0.8,
duration: 0.8
});
gsap.to(), gsap.from() and gsap.fromTo()
These three methods cover a large percentage of everyday GSAP animations.
gsap.to()
Animate from the current state to another state.
gsap.to(".box", {
x: 200,
opacity: 0,
duration: 1
});
Conceptually:
current position
↓
x: 200
current opacity
↓
opacity: 0
gsap.from()
Start from the supplied values and animate toward the element’s existing state.
gsap.from(".hero-title", {
y: 40,
opacity: 0,
duration: 0.8
});
If the normal CSS state is:
y: 0
opacity: 1
the visual movement becomes:
y: 40
opacity: 0
↓
y: 0
opacity: 1
This is useful for entrance animations.
gsap.fromTo()
Define both states explicitly.
gsap.fromTo(
".card",
{
y: 60,
opacity: 0
},
{
y: 0,
opacity: 1,
duration: 0.8
}
);
This is useful when you want complete control over both endpoints.
Easing in GSAP
Without easing, movement can feel mechanical because the animation progresses at a constant mathematical rate.
Easing controls how the speed changes throughout the animation.
A simple example
gsap.to(".box", {
x: 300,
duration: 1,
ease: "power2.out"
});
The animation moves quickly at first and slows toward the end.
Common GSAP easing families
Examples include:
power1;power2;power3;power4;expo;back;bounce;elastic.
Many support directions such as:
power3.in
power3.out
power3.inOut
Do not choose easing only because it looks dramatic in isolation. Motion should match the interface. Navigation, form feedback and page transitions usually benefit from controlled movement rather than theatrical bouncing.
Animating multiple elements with stagger
Suppose you have:
<div class="card">One</div>
<div class="card">Two</div>
<div class="card">Three</div>
You could animate them together:
gsap.from(".card", {
y: 30,
opacity: 0,
duration: 0.6
});
Or introduce a delay between each element:
gsap.from(".card", {
y: 30,
opacity: 0,
duration: 0.6,
stagger: 0.12
});
The sequence becomes approximately:
card 1
↓ 0.12s
card 2
↓ 0.12s
card 3
Stagger is especially useful for:
- navigation links;
- cards;
- lists;
- gallery items;
- hero text fragments.
Using GSAP timelines in WordPress
Individual tweens are useful, but real interfaces often need several animations to work together.
A GSAP timeline is a container that sequences multiple tweens.
Basic timeline
const tl = gsap.timeline();
tl.from(".hero-title", {
y: 40,
opacity: 0,
duration: 0.7
});
tl.from(".hero-text", {
y: 20,
opacity: 0,
duration: 0.5
});
tl.from(".hero-button", {
scale: 0.9,
opacity: 0,
duration: 0.4
});
The default sequence is:
title
↓
text
↓
button
Why timelines are preferable to manual delays
You could write:
delay: 0
delay: 0.7
delay: 1.2
on separate animations.
But that becomes fragile when durations change.
A timeline understands relationships between animations.
You can also control the whole sequence:
tl.play();
tl.pause();
tl.reverse();
tl.restart();
Timeline defaults
You can avoid repeating common settings:
const tl = gsap.timeline({
defaults: {
duration: 0.7,
ease: "power3.out"
}
});
tl.from(".hero-title", {
y: 40,
opacity: 0
});
tl.from(".hero-text", {
y: 20,
opacity: 0
});
Waiting for WordPress content before animating it
One of the most common beginner problems is trying to animate an element before it exists.
DOMContentLoaded
If your script might execute before the DOM is ready:
document.addEventListener(
"DOMContentLoaded",
() => {
gsap.from(".hero-title", {
y: 30,
opacity: 0,
duration: 0.8
});
}
);
This waits until the HTML document has been parsed.
Do you always need DOMContentLoaded?
No.
If your script is loaded in the footer or deferred appropriately, the required elements may already be parsed by the time the script executes.
But explicit initialization is often useful in larger projects because it creates a predictable entry point.
DOMContentLoaded and window load are different
DOMContentLoaded waits for the HTML structure.
window.load waits for additional resources such as images to finish loading.
Do not wait for every image on the website simply because you want to animate a heading.
Use the minimum lifecycle event your animation actually needs.
Loading GSAP plugins correctly
ScrollTrigger and other plugins must load after GSAP Core.
Then your animation code must load after the plugin it uses.
Example with ScrollTrigger
function mytheme_enqueue_animations() {
wp_enqueue_script(
'mytheme-gsap',
get_theme_file_uri(
'/assets/js/gsap.min.js'
),
array(),
'3.13.0',
array(
'in_footer' => true,
)
);
wp_enqueue_script(
'mytheme-scrolltrigger',
get_theme_file_uri(
'/assets/js/ScrollTrigger.min.js'
),
array( 'mytheme-gsap' ),
'3.13.0',
array(
'in_footer' => true,
)
);
wp_enqueue_script(
'mytheme-animations',
get_theme_file_uri(
'/assets/js/app.js'
),
array(
'mytheme-gsap',
'mytheme-scrolltrigger'
),
'1.0.0',
array(
'in_footer' => true,
)
);
}
add_action(
'wp_enqueue_scripts',
'mytheme_enqueue_animations'
);
The dependency tree becomes:
GSAP Core
↓
ScrollTrigger
↓
app.js
Register the plugin
Inside your JavaScript:
gsap.registerPlugin(
ScrollTrigger
);
GSAP’s official documentation recommends explicit plugin registration. This is particularly important in module and build environments, where unused code can otherwise be removed during tree shaking.
Your first ScrollTrigger animation
ScrollTrigger lets an animation react to scroll position.
The official ScrollTrigger documentation covers triggers, start and end positions, scrub, pinning, callbacks and other scroll controls.
Simple reveal
gsap.registerPlugin(
ScrollTrigger
);
gsap.from(".section-title", {
y: 50,
opacity: 0,
duration: 0.8,
scrollTrigger: {
trigger: ".section-title",
start: "top 80%"
}
});
This means approximately:
when top of .section-title
reaches 80% down viewport
↓
start animation
The trigger is not necessarily the animated element
You can use:
trigger: ".services-section"
while animating:
".service-card"
For example:
gsap.from(".service-card", {
y: 40,
opacity: 0,
stagger: 0.1,
scrollTrigger: {
trigger: ".services-section",
start: "top 75%"
}
});
Understanding start positions
A ScrollTrigger start value such as:
start: "top 80%"
compares two points.
top
↑
trigger element
80%
↑
viewport
The animation starts when those positions meet.
Another common example
start: "top center"
means:
top of trigger
reaches
center of viewport
Use markers while developing
ScrollTrigger supports:
markers: true
during development:
scrollTrigger: {
trigger: ".section",
start: "top 80%",
markers: true
}
This displays visual markers showing the trigger positions.
Remove them before production.
Scroll-linked animations with scrub
A normal ScrollTrigger can simply start an animation when the trigger point is reached.
With:
scrub: true
animation progress becomes linked to scroll progress.
Example
gsap.to(".image", {
y: -100,
scrollTrigger: {
trigger: ".image-section",
start: "top bottom",
end: "bottom top",
scrub: true
}
});
Conceptually:
scroll begins
↓
animation progresses
scroll stops
↓
animation stops
scroll reverses
↓
animation reverses
This can be useful for:
- parallax effects;
- progress indicators;
- image movement;
- storytelling sections.
Avoid attaching scrubbed motion to every visible element. Scroll-linked animation is useful when it supports hierarchy or interaction, not when the entire page appears to be resisting the user’s mouse wheel.
Pinning sections with ScrollTrigger
ScrollTrigger can temporarily pin an element while scrolling.
ScrollTrigger.create({
trigger: ".feature",
start: "top top",
end: "+=1000",
pin: true
});
This can create experiences such as:
section enters viewport
↓
section stays fixed
↓
scroll drives content
↓
section releases
Pinning changes layout behavior
GSAP may need to introduce spacing so surrounding content continues correctly.
Always test pinned layouts across:
- desktop;
- tablet;
- mobile;
- different viewport heights.
A pinned section designed only on a 27-inch monitor has a remarkable ability to become architectural rubble on a 375-pixel phone.
Use transform properties for smooth animation
For many visual movements, prefer transform-based properties such as:
x;y;xPercent;yPercent;scale;rotation.
Prefer this
gsap.to(".box", {
x: 100
});
over repeatedly animating layout properties when unnecessary
gsap.to(".box", {
left: "100px"
});
Transform animations usually avoid repeatedly recalculating surrounding document layout.
Opacity is also commonly efficient
gsap.from(".card", {
opacity: 0,
y: 20
});
This combination is a good default for many entrance animations.
Avoid animating too much at once
GSAP is highly optimized, but it cannot make unlimited browser work free.
If you animate:
500 DOM nodes
+
large filters
+
multiple blurred backgrounds
+
continuous scroll effects
+
video
+
canvas
the browser still has to render everything.
Performance depends on the entire rendering pipeline
Consider:
- layout;
- paint;
- compositing;
- image size;
- DOM size;
- other JavaScript;
- mobile hardware.
The existence of GSAP does not repeal physics.
Load GSAP only where you need it
A common WordPress mistake is loading animation libraries across every frontend page even when only the homepage uses them.
Conditional enqueueing
You can load GSAP only on the homepage:
function mytheme_enqueue_animations() {
if ( ! is_front_page() ) {
return;
}
wp_enqueue_script(
'mytheme-gsap',
get_theme_file_uri(
'/assets/js/gsap.min.js'
),
array(),
'3.13.0',
array(
'in_footer' => true,
)
);
wp_enqueue_script(
'mytheme-animations',
get_theme_file_uri(
'/assets/js/home.js'
),
array( 'mytheme-gsap' ),
'1.0.0',
array(
'in_footer' => true,
)
);
}
add_action(
'wp_enqueue_scripts',
'mytheme_enqueue_animations'
);
This avoids sending animation code to pages that do not use it.
You can target other WordPress conditions
Examples include:
is_page()
is_single()
is_singular()
is_archive()
is_page_template()
Conditional loading is especially useful when plugins or themes contain several independent animation experiences.
Do not edit a parent theme directly
If you purchased or installed a theme that receives updates, putting custom GSAP code directly into its files can be risky.
An update may overwrite:
functions.php
app.js
custom templates
The official GSAP WordPress guidance also warns against modifying regularly updated parent themes directly.
Safer options include
- a child theme;
- a custom plugin;
- a dedicated code-management system;
- a library-management module.
For reusable site functionality, a plugin is often preferable to theme-specific code.
Using GSAP through TheOneWP Library Importer
TheOneWP Library Importer can manage frontend libraries without requiring every integration to be hardcoded into functions.php.
The verified module includes GSAP Core among its library catalog options and can load configured libraries through different delivery modes.
Available loading approaches include
- the configured CDN source;
- a downloaded local static copy;
- inline loading where appropriate.
This makes it possible to choose:
GSAP
↓
CDN
or:
GSAP
↓
local static file
without changing the animation logic itself.
The architectural tradeoffs are covered in CDN vs. self-hosted assets in WordPress.
Using GSAP with npm and a WordPress build process
More advanced WordPress themes and plugins often use npm, Vite, webpack or another build system.
Install GSAP:
npm install gsap
Then import it:
import { gsap } from "gsap";
ScrollTrigger with modules
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(
ScrollTrigger
);
Then:
gsap.from(".section-title", {
y: 40,
opacity: 0,
scrollTrigger: {
trigger: ".section-title",
start: "top 80%"
}
});
Your build tool creates the final bundle
The development flow becomes:
source JavaScript
↓
npm dependencies
↓
build tool
↓
dist/app.js
↓
WordPress enqueue
↓
browser
WordPress does not need to understand how GSAP was bundled internally.
It only needs to enqueue the final browser asset.
Using defer and async carefully
Modern WordPress supports script-loading strategies through wp_enqueue_script().
For example:
array(
'strategy' => 'defer',
'in_footer' => true,
)
WordPress’s dependency system considers script relationships when selecting an eligible loading strategy.
defer preserves execution order
Deferred scripts execute after HTML parsing and maintain their document order.
This makes defer more compatible with dependency chains such as:
GSAP
↓
ScrollTrigger
↓
app.js
async does not guarantee dependency order
Asynchronous scripts execute when each file becomes available.
That means:
app.js
could theoretically execute before:
gsap.min.js
if the loading architecture is not managed correctly.
Do not add async indiscriminately to scripts that depend on each other.
Responsive GSAP animations
An animation that works well on desktop may be inappropriate on mobile.
GSAP provides:
gsap.matchMedia()
for creating responsive animation contexts.
Example
const mm = gsap.matchMedia();
mm.add(
"(min-width: 768px)",
() => {
gsap.from(".hero-image", {
x: 100,
opacity: 0,
duration: 1
});
}
);
The animation is created only when the media query matches.
Mobile can use a simpler animation
mm.add(
"(max-width: 767px)",
() => {
gsap.from(".hero-image", {
opacity: 0,
duration: 0.5
});
}
);
This is often better than forcing identical motion across every device.
Respect prefers-reduced-motion
Animation is not purely decorative from an accessibility perspective.
Some users request reduced motion through their operating system or browser.
You can detect that preference with:
window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
Simple approach
const reduceMotion =
window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if ( ! reduceMotion ) {
gsap.from(".hero-title", {
y: 40,
opacity: 0,
duration: 0.8
});
}
Do not hide essential content behind animation
The page should remain usable if animation is:
- disabled;
- reduced;
- blocked;
- unable to initialize.
A heading should not permanently remain:
opacity: 0
just because JavaScript failed.
Avoid flashes of invisible content
Entrance animations frequently start with:
opacity: 0
If you put that state directly into normal CSS:
.hero-title {
opacity: 0;
}
and JavaScript fails, the content may remain invisible.
A safer strategy
Let normal HTML and CSS remain visible by default.
Then let GSAP establish the starting state when animation initializes:
gsap.from(".hero-title", {
opacity: 0,
y: 30,
duration: 0.8
});
For more complex page-load transitions, progressively enhance the page with a JavaScript-enabled class and provide a fallback.
Selectors and WordPress-generated content
WordPress themes, blocks and page builders can generate repeated classes.
If you write:
gsap.to(".title", {
opacity: 0
});
you may accidentally target every:
.title
on the page.
Scope selectors
Prefer:
gsap.from(
".homepage-hero .title",
{
opacity: 0,
y: 30
}
);
or select a parent first:
const hero =
document.querySelector(
".homepage-hero"
);
if ( hero ) {
gsap.from(
hero.querySelector(".title"),
{
opacity: 0,
y: 30
}
);
}
This reduces accidental collisions with theme and plugin markup.
Always guard optional WordPress elements
Templates are dynamic.
A homepage may have:
.hero
while another page does not.
A safe pattern
const hero =
document.querySelector(
".homepage-hero"
);
if ( hero ) {
gsap.from(hero, {
opacity: 0,
duration: 0.8
});
}
This is especially important when one animation bundle is reused across multiple WordPress templates.
Refreshing ScrollTrigger after layout changes
ScrollTrigger calculates positions based on page layout.
That layout can change when:
- images load;
- fonts change metrics;
- accordions expand;
- AJAX content appears;
- page-builder elements resize.
When necessary, you can request recalculation:
ScrollTrigger.refresh();
Do not call refresh continuously
Refreshing trigger geometry requires browser work.
Use it when a meaningful layout change occurs, not inside every scroll event.
Common GSAP mistakes in WordPress
GSAP is not defined
Usually means your animation code executed before GSAP Core or GSAP failed to load.
Check:
- Network panel;
- script dependency handles;
- browser console;
- enqueue order.
ScrollTrigger is not defined
Core may be loaded while the plugin is missing.
Verify:
GSAP
↓
ScrollTrigger
↓
app.js
Forgetting registerPlugin()
Use:
gsap.registerPlugin(
ScrollTrigger
);
especially with module builds.
Hardcoding scripts into header.php
This bypasses WordPress’s dependency management and makes duplicate loading easier.
Use wp_enqueue_script() where possible.
Loading GSAP twice
A theme may load GSAP while a plugin or page builder loads another copy.
Inspect the Network panel before adding another instance.
Loading multiple GSAP versions
A page containing:
GSAP 3.x
+
another GSAP 3.x build
can create unnecessary bandwidth and difficult debugging.
Loading plugins before Core
Use WordPress dependencies rather than hoping file order remains stable.
Using async on dependent scripts
Asynchronous execution does not preserve predictable execution order.
Editing the parent theme
Updates can remove your work.
Animating nonexistent elements
Use existence checks and scoped initialization.
Waiting for window load unnecessarily
A simple heading reveal usually does not need to wait for every image on the site.
Animating layout-heavy properties constantly
Prefer transforms where appropriate.
Using ScrollTrigger for every element
Not every paragraph needs its own animation controller.
Ignoring reduced-motion preferences
Motion should degrade gracefully.
Making content depend on JavaScript animation
Essential content should remain accessible even if animation fails.
Leaving ScrollTrigger markers enabled
Use them for debugging, then remove them from production.
A clean starter architecture
A simple theme structure might be:
my-theme/
│
├── functions.php
│
└── assets/
└── js/
├── gsap.min.js
├── ScrollTrigger.min.js
└── animations.js
functions.php
function mytheme_enqueue_gsap() {
wp_enqueue_script(
'mytheme-gsap',
get_theme_file_uri(
'/assets/js/gsap.min.js'
),
array(),
'3.13.0',
array(
'in_footer' => true,
)
);
wp_enqueue_script(
'mytheme-scrolltrigger',
get_theme_file_uri(
'/assets/js/ScrollTrigger.min.js'
),
array(
'mytheme-gsap'
),
'3.13.0',
array(
'in_footer' => true,
)
);
wp_enqueue_script(
'mytheme-animations',
get_theme_file_uri(
'/assets/js/animations.js'
),
array(
'mytheme-gsap',
'mytheme-scrolltrigger'
),
'1.0.0',
array(
'in_footer' => true,
)
);
}
add_action(
'wp_enqueue_scripts',
'mytheme_enqueue_gsap'
);
animations.js
document.addEventListener(
"DOMContentLoaded",
() => {
gsap.registerPlugin(
ScrollTrigger
);
const hero =
document.querySelector(
".homepage-hero"
);
if ( hero ) {
const tl =
gsap.timeline({
defaults: {
ease: "power3.out"
}
});
tl.from(
hero.querySelector("h1"),
{
y: 40,
opacity: 0,
duration: 0.8
}
);
tl.from(
hero.querySelector("p"),
{
y: 20,
opacity: 0,
duration: 0.6
},
"-=0.4"
);
}
gsap.utils
.toArray(".reveal-section")
.forEach((section) => {
gsap.from(section, {
y: 30,
opacity: 0,
duration: 0.7,
scrollTrigger: {
trigger: section,
start: "top 80%"
}
});
});
}
);
This gives you a maintainable foundation:
dependency-managed loading
+
central initialization
+
element guards
+
timeline
+
ScrollTrigger
A practical learning path for GSAP in WordPress
Do not begin by trying to recreate an award-site homepage with eleven pinned sections and a cursor that behaves like an anxious jellyfish.
Learn in this order:
- Load GSAP correctly.
- Animate one element with
gsap.to(). - Learn
from()andfromTo(). - Understand transforms.
- Learn easing.
- Animate groups with stagger.
- Create a timeline.
- Add ScrollTrigger.
- Learn trigger start and end positions.
- Experiment with scrub.
- Learn responsive animation with
matchMedia(). - Add reduced-motion handling.
- Optimize and conditionally load scripts.
Once those concepts are comfortable, more sophisticated GSAP interfaces become combinations of familiar primitives rather than mysterious blocks of animation code.
GSAP in WordPress checklist
- Load GSAP through WordPress’s enqueue system.
- Use
wp_enqueue_scriptsfor frontend scripts. - Give every script a unique handle.
- Declare dependencies explicitly.
- Load GSAP Core before its plugins.
- Load animation code after the libraries it uses.
- Register GSAP plugins explicitly.
- Pin or track the GSAP version you deploy.
- Choose CDN or local delivery deliberately.
- Avoid loading duplicate GSAP copies.
- Do not modify an updateable parent theme directly.
- Use
gsap.to(),from()andfromTo()appropriately. - Use timelines for coordinated sequences.
- Use stagger for related groups of elements.
- Prefer transform-based movement where appropriate.
- Use ScrollTrigger only where scroll interaction adds value.
- Use markers while debugging ScrollTrigger.
- Remove markers in production.
- Conditionally enqueue GSAP when only some pages need it.
- Scope selectors to avoid theme and plugin collisions.
- Check that optional elements exist before initializing them.
- Respect
prefers-reduced-motion. - Keep content usable without animation.
- Test mobile and tablet layouts.
- Test performance on actual lower-powered devices.
- Use
ScrollTrigger.refresh()when meaningful layout changes require recalculation. - Avoid excessive animation simply because the library makes it possible.
Related WordPress frontend guides
Continue with these related guides and tools:
- wp_enqueue_scripts explained
- CDN vs. self-hosted assets in WordPress
- Library Importer
- WordPress privacy and third-party requests
- WordPress Block Editor CSS explained
- PX vs. EM vs. REM CSS units explained
- Why web fonts cause layout shift and how to avoid it
- Self-Hosted Fonts vs. Google Fonts in WordPress
- Why Third-Party Embeds Slow Down WordPress
Final thoughts
Getting started with GSAP in WordPress requires understanding two separate systems.
First:
WordPress
↓
loads and manages
JavaScript dependencies
Second:
GSAP
↓
creates and controls
the animations
Keep those responsibilities separate.
Use WordPress to load:
GSAP Core
↓
optional GSAP plugins
↓
your animation code
Then use GSAP to build motion through:
tweens
+
timelines
+
easing
+
stagger
+
ScrollTrigger
Start with simple transforms and entrance effects before moving into pinned sections and complex scroll-driven sequences.
Declare dependencies correctly, avoid duplicate libraries, scope your selectors and load GSAP only on pages that actually need it.
Most importantly, treat motion as part of the interface rather than decoration added after the page is finished. A useful animation clarifies hierarchy, provides feedback or guides attention. It should not make users wait for the website to finish performing.
If you want to manage GSAP and other frontend libraries without manually maintaining every enqueue declaration, TheOneWP Library Importer provides GSAP Core as part of its library catalog and lets you choose between CDN delivery, a downloaded local static copy or inline loading depending on the architecture you need.

