Smooth SVG Morphing: From Icons to Logos on React/Next.js

A client asked us to add smooth icon morphing to their site: a circle turns into a star, the star into a heart. The design was ready, but out-of-the-box SVG morphing didn't work — the shapes jerked and broke. The reason: the start and end shapes had different numbers of nodes. We solved this with GS

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1419
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    983
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1247
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    984
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    998

A client asked us to add smooth icon morphing to their site: a circle turns into a star, the star into a heart. The design was ready, but out-of-the-box SVG morphing didn't work — the shapes jerked and broke. The reason: the start and end shapes had different numbers of nodes. We solved this with GSAP MorphSVGPlugin by GreenSock. Here's how to set up morphing without headaches and save your budget on debugging. Over 5 years, we've delivered over 50 projects with SVG animation, and morphing is one of the most common tasks. Below are proven tools, ready snippets, and typical pitfalls.

Problems We Solve

The main difficulty is mismatched point counts in path elements. SMIL and CSS clip-path only work with identical path structures. The second issue is performance on older devices: animation drops below 30 FPS. The third is complexity of integration into modern frameworks, especially with SSR/SSG, where client-side animations require careful handling. For instance, on one project we faced logo morphing in a catalog — unoptimized paths caused stuttering on iPhone 8. Our solution: move everything to GSAP with MorphSVG, and conversion to the product card increased by 15%.

How to Choose a Morphing Tool

Tool Complexity Cost Performance Point Alignment
GSAP MorphSVG Medium Paid High Automatic
Flubber.js Low Free Medium Manual only
SMIL High Free Low Requires identical paths

Calculate the cost of your project — contact us.

Why GSAP MorphSVG Is Better Than Alternatives

GSAP MorphSVGPlugin is 3x faster to set up for complex paths than Flubber.js. It automatically aligns node counts and supports timelines for sequences. In a product catalog project, we implemented brand logo morphing using React 18 and GSAP. The hover animation increased conversion to the product card by 15%.

Implementation on React with GSAP

We use React 18, Next.js 14, TypeScript. For morphing — GSAP with MorphSVGPlugin. Example implementation:

// lib/gsap-morph.ts import { gsap } from 'gsap' import { MorphSVGPlugin } from 'gsap/MorphSVGPlugin' gsap.registerPlugin(MorphSVGPlugin) export { gsap } // components/MorphingIcon.tsx import { useEffect, useRef, useState } from 'react' import { gsap } from '../lib/gsap-morph' const shapes = { circle: 'M 50,10 A 40,40 0 1,1 49.9,10', star: 'M50,5 L61,35 L95,35 L68,57 L79,91 L50,70 L21,91 L32,57 L5,35 L39,35 Z', heart: 'M50,85 C10,60 5,30 25,15 C35,7 45,10 50,18 C55,10 65,7 75,15 C95,30 90,60 50,85 Z', arrow: 'M 10,50 L 40,20 L 40,35 L 90,35 L 90,65 L 40,65 L 40,80 Z', } type ShapeKey = keyof typeof shapes export function MorphingIcon() { const pathRef = useRef<SVGPathElement>(null) const [current, setCurrent] = useState<ShapeKey>('circle') const morphTo = (target: ShapeKey) => { if (!pathRef.current || target === current) return gsap.to(pathRef.current, { morphSVG: { shape: shapes[target], origin: '50% 50%', type: 'rotational', }, duration: 0.8, ease: 'power2.inOut', }) setCurrent(target) } return ( <div> <svg viewBox="0 0 100 100" className="w-32 h-32"> <path ref={pathRef} d={shapes.circle} fill="none" stroke="#3b82f6" strokeWidth="2" /> </svg> <div className="flex gap-2 mt-4"> {(Object.keys(shapes) as ShapeKey[]).map(key => ( <button key={key} onClick={() => morphTo(key)} className={`px-3 py-1 text-sm rounded ${ current === key ? 'bg-blue-500 text-white' : 'bg-gray-100' }`} > {key} </button> ))} </div> </div> ) } 

Alternative Tools: Flubber.js and SMIL

For simple two-state morphs, Flubber.js works well. It's free but requires manual point alignment. Install with npm install flubber. Example usage below:

// components/FlubberMorph.tsx import { useEffect, useRef, useState } from 'react' import { interpolate } from 'flubber' export function FlubberMorph() { const pathRef = useRef<SVGPathElement>(null) const animFrameRef = useRef<number | null>(null) const morphBetween = ( startPath: string, endPath: string, durationMs = 800 ) => { const interpolator = interpolate(startPath, endPath, { maxSegmentLength: 2 }) const start = performance.now() const animate = (now: number) => { const elapsed = now - start const t = Math.min(elapsed / durationMs, 1) const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t if (pathRef.current) { pathRef.current.setAttribute('d', interpolator(ease)) } if (t < 1) { animFrameRef.current = requestAnimationFrame(animate) } } if (animFrameRef.current) cancelAnimationFrame(animFrameRef.current) animFrameRef.current = requestAnimationFrame(animate) } const squarePath = 'M 20,20 L 80,20 L 80,80 L 20,80 Z' const blobPath = 'M 50,10 C 80,10 90,30 90,50 C 90,70 70,90 50,90 C 30,90 10,70 10,50 C 10,30 20,10 50,10 Z' const [isBlob, setIsBlob] = useState(false) useEffect(() => { if (pathRef.current) { pathRef.current.setAttribute('d', squarePath) } }, []) const toggle = () => { morphBetween( isBlob ? blobPath : squarePath, isBlob ? squarePath : blobPath ) setIsBlob(!isBlob) } return ( <div> <svg viewBox="0 0 100 100" className="w-48 h-48"> <path ref={pathRef} fill="#8b5cf6" /> </svg> <button onClick={toggle} className="mt-4 px-4 py-2 bg-purple-500 text-white rounded"> Morph </button> </div> ) } 

If both shapes have the same number of points, you can use SMIL without any libraries. Example: square morphs to diamond and back. See MDN documentation.

export function SMILMorph() { return ( <svg viewBox="0 0 100 100" className="w-32 h-32"> <path fill="#f59e0b"> <animate attributeName="d" dur="1.5s" repeatCount="indefinite" values=" M 20,20 L 80,20 L 80,80 L 20,80 Z; M 50,10 L 90,50 L 50,90 L 10,50 Z; M 20,20 L 80,20 L 80,80 L 20,80 Z " keyTimes="0; 0.5; 1" calcMode="spline" keySplines="0.4 0 0.2 1; 0.4 0 0.2 1" /> </path> </svg> ) } 

How to Prepare SVG Paths for Morphing?

For successful morphing, shapes must be optimized. Use tools:

  1. SVGO — minimize and normalize paths.
  2. Inkscape → Extensions > Modify Path > Add Nodes — align point count.
  3. GSAP MorphSVGPlugin.convertToPath() — converts <circle>, <rect>, etc. into <path>.
import { MorphSVGPlugin } from 'gsap/MorphSVGPlugin' MorphSVGPlugin.convertToPath('#my-circle, #my-rect') 
Additional preparation tips Before morphing, check the number of nodes with `path.getTotalLength()`. The difference should be minimal. For complex shapes, use GSAP — it aligns nodes automatically. If budget is tight, choose Flubber.js and prepare paths manually.

Process and Timelines

  1. Analysis — agree on design and verify source shapes.
  2. Optimization — clean paths with SVGO, align nodes.
  3. Library selection — recommend GSAP for complex projects.
  4. Implementation — write animation, test on mobile devices.
  5. Integration — embed into application, check Core Web Vitals (LCP, CLS).
Work Type Timeline
Simple two-state morphing from 4 hours
Interactive morphing with shape selection 1–2 days
Complex sequence with color and interactivity up to 5 days

The cost includes documentation, repository access with demo, training for the client's team, and 30-day support. We guarantee smooth 60 FPS on all devices. Order morphing for your website — get a consultation and project estimate within 1 day. Contact us.

Typical Mistakes

  • Mismatch in node count — check with path.getTotalLength(). Even one extra node breaks the animation.
  • Ignoring ease functions — without them the animation looks unnatural. Use power2.inOut or back.out.
  • No window resize handling — shapes may shift if coordinates aren't recalculated. Bind animation to resize via ResizeObserver.

We guarantee that with our experience (5+ years, 50+ projects) your morphing will run without stuttering. Contact us for a consultation.