[{"data":1,"prerenderedAt":787},["ShallowReactive",2],{"blog-\u002Fblog\u002F2026\u002Fone-carousel-instead-of-three":3,"blog-project-\u002Fblog\u002F2026\u002Fone-carousel-instead-of-three":709},{"id":4,"title":5,"art":6,"body":7,"date":692,"description":693,"draft":694,"extension":695,"image":696,"kind":697,"meta":698,"navigation":73,"ogImage":699,"path":700,"project":701,"seo":702,"slug":696,"stem":703,"tags":704,"translationKey":707,"updated":696,"__hash__":708},"blog_en\u002Fblog\u002F2026\u002Fone-carousel-instead-of-three.md","One carousel instead of three","\u002Fart\u002Fblog\u002Fjedna-karuzela-zamiast-trzech.svg",{"type":8,"value":9,"toc":685},"minimark",[10,14,19,22,25,382,385,460,464,467,476,486,493,559,563,570,642,648,652,655,659,681],[11,12,13],"p",{},"superauto.pl is a site with car offers – new, used, ex-lease cars and trucks. I worked on it at Arris Agency, and the biggest ordered rebuild I did there was replacing three different carousels with one. It went live in spring 2025, step by step. The situation is real, but the code below is simplified examples, not the code from the site.",[15,16,18],"h2",{"id":17},"three-carousels-three-different-codes","Three carousels, three different codes",[11,20,21],{},"Each of the three carousels had its own code, written from scratch for one element of the page. They behaved in a similar way, but each one was heavy and each one had to be fixed separately. I suggested Swiper: it loads images in advance and is easy to edit.",[11,23,24],{},"The hardest part at the beginning was thinking through the logic. One component had to handle four places: the car gallery with thumbnails, the full-screen photo view, the carousel of vehicle cards and the carousel of small offers. I made one pattern and described the differences as variants of settings:",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-js shiki shiki-themes github-light github-dark","import Swiper from 'swiper'\nimport { Navigation, Thumbs } from 'swiper\u002Fmodules'\n\n\u002F\u002F settings shared by every carousel on the site\nconst base = {\n  modules: [Navigation, Thumbs],\n  lazyPreloadPrevNext: 1, \u002F\u002F the next photo loads in advance\n}\n\n\u002F\u002F variants differ only in settings\nconst variants = {\n  gallery: { slidesPerView: 1 },\n  vehicles: {\n    slidesPerView: 1.2,\n    spaceBetween: 16,\n    breakpoints: { 1024: { slidesPerView: 3 } },\n  },\n  offers: { slidesPerView: 1 },\n}\n\nexport function createCarousel(el, extra = {}) {\n  const variant = variants[el.dataset.carousel] ?? variants.gallery\n\n  return new Swiper(el, {\n    ...base,\n    ...variant,\n    navigation: {\n      prevEl: el.querySelector('[data-prev]'),\n      nextEl: el.querySelector('[data-next]'),\n    },\n    ...extra,\n  })\n}\n","js","",[33,34,35,55,68,75,82,98,104,119,125,130,136,148,159,165,177,188,206,212,222,227,232,262,282,287,302,311,319,325,342,357,363,371,377],"code",{"__ignoreMap":31},[36,37,40,44,48,51],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"szBVR","import",[36,45,47],{"class":46},"sVt8B"," Swiper ",[36,49,50],{"class":42},"from",[36,52,54],{"class":53},"sZZnC"," 'swiper'\n",[36,56,58,60,63,65],{"class":38,"line":57},2,[36,59,43],{"class":42},[36,61,62],{"class":46}," { Navigation, Thumbs } ",[36,64,50],{"class":42},[36,66,67],{"class":53}," 'swiper\u002Fmodules'\n",[36,69,71],{"class":38,"line":70},3,[36,72,74],{"emptyLinePlaceholder":73},true,"\n",[36,76,78],{"class":38,"line":77},4,[36,79,81],{"class":80},"sqY6-","\u002F\u002F settings shared by every carousel on the site\n",[36,83,85,88,92,95],{"class":38,"line":84},5,[36,86,87],{"class":42},"const",[36,89,91],{"class":90},"sj4cs"," base",[36,93,94],{"class":42}," =",[36,96,97],{"class":46}," {\n",[36,99,101],{"class":38,"line":100},6,[36,102,103],{"class":46},"  modules: [Navigation, Thumbs],\n",[36,105,107,110,113,116],{"class":38,"line":106},7,[36,108,109],{"class":46},"  lazyPreloadPrevNext: ",[36,111,112],{"class":90},"1",[36,114,115],{"class":46},", ",[36,117,118],{"class":80},"\u002F\u002F the next photo loads in advance\n",[36,120,122],{"class":38,"line":121},8,[36,123,124],{"class":46},"}\n",[36,126,128],{"class":38,"line":127},9,[36,129,74],{"emptyLinePlaceholder":73},[36,131,133],{"class":38,"line":132},10,[36,134,135],{"class":80},"\u002F\u002F variants differ only in settings\n",[36,137,139,141,144,146],{"class":38,"line":138},11,[36,140,87],{"class":42},[36,142,143],{"class":90}," variants",[36,145,94],{"class":42},[36,147,97],{"class":46},[36,149,151,154,156],{"class":38,"line":150},12,[36,152,153],{"class":46},"  gallery: { slidesPerView: ",[36,155,112],{"class":90},[36,157,158],{"class":46}," },\n",[36,160,162],{"class":38,"line":161},13,[36,163,164],{"class":46},"  vehicles: {\n",[36,166,168,171,174],{"class":38,"line":167},14,[36,169,170],{"class":46},"    slidesPerView: ",[36,172,173],{"class":90},"1.2",[36,175,176],{"class":46},",\n",[36,178,180,183,186],{"class":38,"line":179},15,[36,181,182],{"class":46},"    spaceBetween: ",[36,184,185],{"class":90},"16",[36,187,176],{"class":46},[36,189,191,194,197,200,203],{"class":38,"line":190},16,[36,192,193],{"class":46},"    breakpoints: { ",[36,195,196],{"class":90},"1024",[36,198,199],{"class":46},": { slidesPerView: ",[36,201,202],{"class":90},"3",[36,204,205],{"class":46}," } },\n",[36,207,209],{"class":38,"line":208},17,[36,210,211],{"class":46},"  },\n",[36,213,215,218,220],{"class":38,"line":214},18,[36,216,217],{"class":46},"  offers: { slidesPerView: ",[36,219,112],{"class":90},[36,221,158],{"class":46},[36,223,225],{"class":38,"line":224},19,[36,226,124],{"class":46},[36,228,230],{"class":38,"line":229},20,[36,231,74],{"emptyLinePlaceholder":73},[36,233,235,238,241,245,248,252,254,257,259],{"class":38,"line":234},21,[36,236,237],{"class":42},"export",[36,239,240],{"class":42}," function",[36,242,244],{"class":243},"sScJk"," createCarousel",[36,246,247],{"class":46},"(",[36,249,251],{"class":250},"siNPP","el",[36,253,115],{"class":46},[36,255,256],{"class":250},"extra",[36,258,94],{"class":42},[36,260,261],{"class":46}," {}) {\n",[36,263,265,268,271,273,276,279],{"class":38,"line":264},22,[36,266,267],{"class":42},"  const",[36,269,270],{"class":90}," variant",[36,272,94],{"class":42},[36,274,275],{"class":46}," variants[el.dataset.carousel] ",[36,277,278],{"class":42},"??",[36,280,281],{"class":46}," variants.gallery\n",[36,283,285],{"class":38,"line":284},23,[36,286,74],{"emptyLinePlaceholder":73},[36,288,290,293,296,299],{"class":38,"line":289},24,[36,291,292],{"class":42},"  return",[36,294,295],{"class":42}," new",[36,297,298],{"class":243}," Swiper",[36,300,301],{"class":46},"(el, {\n",[36,303,305,308],{"class":38,"line":304},25,[36,306,307],{"class":42},"    ...",[36,309,310],{"class":46},"base,\n",[36,312,314,316],{"class":38,"line":313},26,[36,315,307],{"class":42},[36,317,318],{"class":46},"variant,\n",[36,320,322],{"class":38,"line":321},27,[36,323,324],{"class":46},"    navigation: {\n",[36,326,328,331,334,336,339],{"class":38,"line":327},28,[36,329,330],{"class":46},"      prevEl: el.",[36,332,333],{"class":243},"querySelector",[36,335,247],{"class":46},[36,337,338],{"class":53},"'[data-prev]'",[36,340,341],{"class":46},"),\n",[36,343,345,348,350,352,355],{"class":38,"line":344},29,[36,346,347],{"class":46},"      nextEl: el.",[36,349,333],{"class":243},[36,351,247],{"class":46},[36,353,354],{"class":53},"'[data-next]'",[36,356,341],{"class":46},[36,358,360],{"class":38,"line":359},30,[36,361,362],{"class":46},"    },\n",[36,364,366,368],{"class":38,"line":365},31,[36,367,307],{"class":42},[36,369,370],{"class":46},"extra,\n",[36,372,374],{"class":38,"line":373},32,[36,375,376],{"class":46},"  })\n",[36,378,380],{"class":38,"line":379},33,[36,381,124],{"class":46},[11,383,384],{},"The extensions are just extra settings. The car gallery gets a second, small carousel with thumbnails, and the full-screen view is the same carousel with bigger photos, opened on the clicked photo:",[26,386,388],{"className":28,"code":387,"language":30,"meta":31,"style":31},"const thumbs = new Swiper(thumbsEl, {\n  slidesPerView: 6,\n  spaceBetween: 8,\n  watchSlidesProgress: true,\n})\n\ncreateCarousel(galleryEl, { thumbs: { swiper: thumbs } })\ncreateCarousel(fullscreenEl, { initialSlide: clickedIndex })\n",[33,389,390,406,416,426,436,441,445,453],{"__ignoreMap":31},[36,391,392,394,397,399,401,403],{"class":38,"line":39},[36,393,87],{"class":42},[36,395,396],{"class":90}," thumbs",[36,398,94],{"class":42},[36,400,295],{"class":42},[36,402,298],{"class":243},[36,404,405],{"class":46},"(thumbsEl, {\n",[36,407,408,411,414],{"class":38,"line":57},[36,409,410],{"class":46},"  slidesPerView: ",[36,412,413],{"class":90},"6",[36,415,176],{"class":46},[36,417,418,421,424],{"class":38,"line":70},[36,419,420],{"class":46},"  spaceBetween: ",[36,422,423],{"class":90},"8",[36,425,176],{"class":46},[36,427,428,431,434],{"class":38,"line":77},[36,429,430],{"class":46},"  watchSlidesProgress: ",[36,432,433],{"class":90},"true",[36,435,176],{"class":46},[36,437,438],{"class":38,"line":84},[36,439,440],{"class":46},"})\n",[36,442,443],{"class":38,"line":100},[36,444,74],{"emptyLinePlaceholder":73},[36,446,447,450],{"class":38,"line":106},[36,448,449],{"class":243},"createCarousel",[36,451,452],{"class":46},"(galleryEl, { thumbs: { swiper: thumbs } })\n",[36,454,455,457],{"class":38,"line":121},[36,456,449],{"class":243},[36,458,459],{"class":46},"(fullscreenEl, { initialSlide: clickedIndex })\n",[15,461,463],{"id":462},"a-dozen-carousels-on-one-page","A dozen carousels on one page",[11,465,466],{},"On offer lists every card has its own small gallery. With a dozen or more offers on a page, there are a dozen or more carousels at the same time. On some computers the carousel started to stutter, and at first it looked like there were simply too many carousels.",[11,468,469],{},[470,471],"img",{"alt":472,"height":473,"src":474,"width":475},"Offer carousels on superauto.pl – every card has its own small gallery",1080,"\u002Fprojects\u002F2025\u002Fsuperauto\u002Foffer-carousels.webp",1920,[11,477,478,479,482,483,485],{},"It turned out that the number of carousels wasn't the problem. The background was. Photos load in two steps: first a small low-quality preview with a ",[33,480,481],{},"blur"," filter appears underneath, and after a moment the full version. The ",[33,484,481],{}," filter is expensive for the browser, and when the carousel moved, slower computers couldn't keep up.",[11,487,488,489,492],{},"I didn't want to remove this effect. What helped was a CSS rule that moved the blurred background to a separate layer handled by the graphics card. I think it was ",[33,490,491],{},"transform: translateZ(0)",", but I don't remember exactly any more:",[26,494,498],{"className":495,"code":496,"language":497,"meta":31,"style":31},"language-css shiki shiki-themes github-light github-dark","\u002F* blurred preview under the photo – waits until the full version loads *\u002F\n.offer-card__preview {\n  filter: blur(12px);\n  \u002F* separate layer: the blur isn't painted again when the carousel moves *\u002F\n  transform: translateZ(0);\n}\n","css",[33,499,500,505,512,533,538,555],{"__ignoreMap":31},[36,501,502],{"class":38,"line":39},[36,503,504],{"class":80},"\u002F* blurred preview under the photo – waits until the full version loads *\u002F\n",[36,506,507,510],{"class":38,"line":57},[36,508,509],{"class":243},".offer-card__preview",[36,511,97],{"class":46},[36,513,514,517,520,522,524,527,530],{"class":38,"line":70},[36,515,516],{"class":90},"  filter",[36,518,519],{"class":46},": ",[36,521,481],{"class":90},[36,523,247],{"class":46},[36,525,526],{"class":90},"12",[36,528,529],{"class":42},"px",[36,531,532],{"class":46},");\n",[36,534,535],{"class":38,"line":77},[36,536,537],{"class":80},"  \u002F* separate layer: the blur isn't painted again when the carousel moves *\u002F\n",[36,539,540,543,545,548,550,553],{"class":38,"line":84},[36,541,542],{"class":90},"  transform",[36,544,519],{"class":46},[36,546,547],{"class":90},"translateZ",[36,549,247],{"class":46},[36,551,552],{"class":90},"0",[36,554,532],{"class":46},[36,556,557],{"class":38,"line":100},[36,558,124],{"class":46},[15,560,562],{"id":561},"the-server-generates-the-images","The server generates the images",[11,564,565,566,569],{},"The server prepared all image sizes, so I adjusted to what I got from the backend. The backend also made sure that the number of images wouldn't overload the server. When we were making the mobile version faster and added smaller images in ",[33,567,568],{},"srcset",", suddenly there were a lot of thumbnails. Generating new sizes for all photos in every offer overloaded the server and filled up the disk, so the generation stopped. Later it took more than an hour.",[26,571,575],{"className":572,"code":573,"language":574,"meta":31,"style":31},"language-html shiki shiki-themes github-light github-dark","\u003Cimg\n  src=\"\u002Fphotos\u002Foffer-1234\u002F1-800.jpg\"\n  srcset=\"\u002Fphotos\u002Foffer-1234\u002F1-400.jpg 400w, \u002Fphotos\u002Foffer-1234\u002F1-800.jpg 800w\"\n  sizes=\"(max-width: 640px) 100vw, 400px\"\n  loading=\"lazy\"\n  alt=\"Sample car from an offer\"\n>\n","html",[33,576,577,586,597,607,617,627,637],{"__ignoreMap":31},[36,578,579,582],{"class":38,"line":39},[36,580,581],{"class":46},"\u003C",[36,583,585],{"class":584},"s9eBZ","img\n",[36,587,588,591,594],{"class":38,"line":57},[36,589,590],{"class":243},"  src",[36,592,593],{"class":46},"=",[36,595,596],{"class":53},"\"\u002Fphotos\u002Foffer-1234\u002F1-800.jpg\"\n",[36,598,599,602,604],{"class":38,"line":70},[36,600,601],{"class":243},"  srcset",[36,603,593],{"class":46},[36,605,606],{"class":53},"\"\u002Fphotos\u002Foffer-1234\u002F1-400.jpg 400w, \u002Fphotos\u002Foffer-1234\u002F1-800.jpg 800w\"\n",[36,608,609,612,614],{"class":38,"line":77},[36,610,611],{"class":243},"  sizes",[36,613,593],{"class":46},[36,615,616],{"class":53},"\"(max-width: 640px) 100vw, 400px\"\n",[36,618,619,622,624],{"class":38,"line":84},[36,620,621],{"class":243},"  loading",[36,623,593],{"class":46},[36,625,626],{"class":53},"\"lazy\"\n",[36,628,629,632,634],{"class":38,"line":100},[36,630,631],{"class":243},"  alt",[36,633,593],{"class":46},[36,635,636],{"class":53},"\"Sample car from an offer\"\n",[36,638,639],{"class":38,"line":106},[36,640,641],{"class":46},">\n",[11,643,644,645,647],{},"Every size in ",[33,646,568],{}," is a separate file for every photo in every offer. On a site with a lot of offers, one extra size means a lot of new files at once.",[15,649,651],{"id":650},"going-live-step-by-step","Going live step by step",[11,653,654],{},"The new carousel went live step by step. I didn't have a list of the places where carousels appear, so I looked for them in the code. Every type of offer – new, used, ex-lease cars and trucks – had its own template files, so I made the same change in several places. In my opinion one template would be enough, showing only the sections that fit a given offer. Still, I adjusted to the code that was already there, and I passed on the idea of one template as a suggestion.",[15,656,658],{"id":657},"what-came-out-of-it","What came out of it",[660,661,662,666,673,678],"ul",{},[663,664,665],"li",{},"One Swiper component instead of three carousels with their own code – you make a change in one place.",[663,667,668,669,672],{},"Stuttering doesn't always come from the number of elements. Sometimes one expensive effect is to blame, like ",[33,670,671],{},"filter: blur()",".",[663,674,675,676,672],{},"Every new image size is extra work for the server – it's worth agreeing on the sizes with the backend before you add ",[33,677,568],{},[663,679,680],{},"Before a change like this, a list of the places where the element appears helps a lot.",[682,683,684],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sqY6-, html code.shiki .sqY6-{--shiki-default:#6A737D;--shiki-dark:#90A1B9}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .siNPP, html code.shiki .siNPP{--shiki-default:#CA3500;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":31,"searchDepth":57,"depth":57,"links":686},[687,688,689,690,691],{"id":17,"depth":57,"text":18},{"id":462,"depth":57,"text":463},{"id":561,"depth":57,"text":562},{"id":650,"depth":57,"text":651},{"id":657,"depth":57,"text":658},"2026-11-03","How I replaced three carousels with their own code with one Swiper component on superauto.pl – and why the carousel was stuttering because of a blurred background, not because of the number of carousels.",false,"md",null,"case-study",{},"\u002Fog\u002Fblog\u002Fjedna-karuzela-zamiast-trzech-en.jpg","\u002Fblog\u002F2026\u002Fone-carousel-instead-of-three","\u002Fprojects\u002F2025\u002Fsuperauto",{"title":5,"description":693},"blog\u002F2026\u002Fone-carousel-instead-of-three",[705,706,497],"javascript","swiper","jedna-karuzela-zamiast-trzech","Y6XXgQs3-UemZexRpl6eWUZAFwqpCFbtG-MIpBkLmXI",{"id":710,"title":711,"agency":712,"body":713,"compare":696,"cover":745,"date":748,"description":749,"draft":694,"extension":695,"facts":750,"featured":694,"gallery":762,"galleryLead":773,"highlights":696,"image":774,"links":696,"meta":775,"navigation":73,"note":776,"ogImage":777,"path":701,"seo":778,"stack":779,"stem":783,"tags":784,"__hash__":786},"projects_en\u002Fprojects\u002F2025\u002Fsuperauto\u002Findex.md","superauto.pl","Arris Agency",{"type":8,"value":714,"toc":742},[715,718,722],[11,716,717],{},"superauto.pl is a site with car offers, also for leasing. At Arris Agency I made many changes there, and the biggest ordered rebuild was replacing several different carousels with one. I suggested Swiper – it loads images in advance and is easy to edit – and I built one bigger component that replaced three different carousels.",[15,719,721],{"id":720},"what-changed","What changed",[660,723,724,727,730,733,736,739],{},[663,725,726],{},"One carousel pattern instead of three different ones – easier to maintain and edit",[663,728,729],{},"The car gallery with thumbnails as an extension of the pattern",[663,731,732],{},"A full-screen photo view (lightbox) – the same carousel, bigger photos",[663,734,735],{},"Carousels for vehicle cards and small offers – new offers load more smoothly after fetching data",[663,737,738],{},"Photos load in two steps: first a blurred low-quality preview in the background, then the full version",[663,740,741],{},"Video tiles that take you to the section of the offer with that video",{"title":31,"searchDepth":57,"depth":57,"links":743},[744],{"id":720,"depth":57,"text":721},{"desktop":746,"width":475,"height":473,"alt":747},"\u002Fprojects\u002F2025\u002Fsuperauto\u002Fcar-page.webp","superauto.pl – car page with a gallery and thumbnails","2025-03-01","My part of the site – three different carousels replaced with one Swiper component: the car gallery, a full-screen view and offer carousels.",[751,753,756,759],{"value":112,"text":752},"carousel component instead of three different ones – one pattern for the whole site",{"value":754,"text":755},"4","extensions of the pattern – gallery with thumbnails, full-screen view, vehicles and small offers",{"value":757,"text":758},"2","steps of loading a photo – first a blurred preview, then full quality",{"value":760,"text":761},"Swiper","the library I suggested – images loaded in advance and easy editing",[763,766,770],{"src":746,"width":475,"height":473,"alt":764,"caption":765},"Car page with a gallery","Car page – gallery with thumbnails, arrows and a photo counter",{"src":767,"width":475,"height":473,"alt":768,"caption":769},"\u002Fprojects\u002F2025\u002Fsuperauto\u002Fgallery-fullscreen.webp","Full-screen gallery","Full-screen view – the same carousel pattern, thumbnails under the photo",{"src":474,"width":475,"height":473,"alt":771,"caption":772},"Offer carousels","Offer carousels – model versions and \"Others also viewed\", each card has its own small gallery","Screenshots of the site – click one to see it bigger.","\u002Fprojects\u002F2025\u002Fsuperauto\u002Fthumbnail.webp",{},"A project at Arris Agency – I only show my part of the site.","\u002Fog\u002Fprojects\u002Fsuperauto-en.jpg",{"title":711,"description":749},[780,760,781,782],"JavaScript","Twig","SCSS","projects\u002F2025\u002Fsuperauto\u002Findex",[705,706,785],"twig","WsOzCUhLG7MUnvxIR5B6bhBgtnTm4yadwqhNcPCd7Pc",1793699931429]