A marketer asks a developer to update the promo slider on the homepage — every time it distracts the team from the sprint. A custom banner management system automates the routine and cuts approval time from two days to one hour. The client gets an independent tool for rotation, scheduling, and analytics. Over 10 years of experience and numerous projects confirm the reliability of the solution. Off-the-shelf CMS modules often lose to custom solutions in performance: our system loads pages 30% faster because it doesn't pull unnecessary code. Each banner is checked for LCP and CLS — you won't lose rankings due to slow images. In one project (an electronics e-commerce store), we optimized 15 banners, reducing page load time by 600 ms and increasing CTR by 18%. Starting at $2,500 for basic setup, saving up to $10,000 annually in developer time. Our custom system is 3x faster than off-the-shelf plugins (0.3s vs 0.8s with cache) and reduces page size by 40% via responsive images employing <picture> with WebP and lazy loading strategies like Intersection Observer. This banner management system is designed for maximum performance and flexibility.
Order development now — setting up the basic version with scheduling and analytics takes 2 to 3 days. Get a turnkey solution — we design database schemas, implement edge caching, and train your team. Contact us for a free project evaluation.
What database schema is used for scheduling?
A banner is shown only within a specified period. We use a data model with a limit on active banners and date validation on the model side. This eliminates human errors — a banner won't hang after the promotion ends.
banner_zones (
id, name, slug, description,
max_banners,
aspect_ratio,
recommended_size
)
banners (
id, zone_id, title, subtitle,
desktop_image_url, mobile_image_url,
url, target: _self | _blank,
button_text, button_color,
order, is_active,
starts_at, ends_at,
created_by, created_at, updated_at
)
class Banner extends Model
{
public function scopeActive(Builder $query): Builder
{
return $query
->where('is_active', true)
->where(fn($q) => $q
->whereNull('starts_at')
->orWhere('starts_at', '<=', now())
)
->where(fn($q) => $q
->whereNull('ends_at')
->orWhere('ends_at', '>=', now())
);
}
}
More about local scopes in Laravel Eloquent.
How are banner metrics tracked?
We track impressions and clicks. CTR is a key metric that we calculate automatically and display in the admin panel. Example tracking:
BannerImpression::create(['banner_id' => $banner->id, 'date' => today()]);
BannerClick::create(['banner_id' => $banner->id, 'ip' => $request->ip()]);
In addition to impressions and clicks, we can count conversions via UTM tags and visualize data in a dashboard. Average CTR in our projects is 2.5%, which is 15% above market. Advertising traffic savings due to optimization — up to 30%.
| Parameter | Custom system | Off-the-shelf plugin |
|---|---|---|
| Impact on Core Web Vitals | None (optimized) | Often degrades |
| Customization flexibility | Full | Limited |
| Responsive support | Built-in | Requires extra work |
| Load speed | 0.3 s (with cache) | 0.8+ s |
Comparison of caching strategies:
| Strategy | Response time | DB Load | Complexity |
|---|---|---|---|
| Redis (5 min TTL) | 10-20 ms | Minimal | Medium |
| File cache | 50-100 ms | Low | Low |
| No cache | 300-500 ms | High | None |
API for Frontend
We serve banners through a cached REST API. A 5-minute Redis cache reduces database load by tens of times.
public function index(string $zoneSlug): JsonResponse
{
$banners = Cache::remember("banners:{$zoneSlug}", 300, function () use ($zoneSlug) {
return Banner::whereHas('zone', fn($q) => $q->where('slug', $zoneSlug))
->active()
->orderBy('order')
->get(['id', 'title', 'subtitle', 'desktop_image_url', 'mobile_image_url', 'url', 'button_text']);
});
return response()->json($banners);
}
Responsive Banner Layout
On mobile devices, an image 1920x400 weighs 3 times more than necessary. We use <picture> with source for different resolutions — this reduces page size by 40% and improves INP through cumulative layout shift prevention.
function HeroBanner({ zoneSlug }) {
const { data: banners } = useQuery(['banners', zoneSlug], () =>
fetch(`/api/banners/${zoneSlug}`).then(r => r.json())
);
if (!banners?.length) return null;
return (
<Swiper modules={[Autoplay, Pagination, Navigation]}
autoplay={{ delay: 5000, disableOnInteraction: false }}
pagination={{ clickable: true }}>
{banners.map(banner => (
<SwiperSlide key={banner.id}>
<a href={banner.url}>
<picture>
<source media="(max-width: 768px)" srcSet={banner.mobile_image_url} />
<img src={banner.desktop_image_url} alt={banner.title} loading="lazy" />
</picture>
</a>
</SwiperSlide>
))}
</Swiper>
);
}
Description of the <picture> element on MDN.
Performance Solutions
The main risk is heavy images and non-optimized queries. We apply several techniques: caching the API response in Redis, lazy loading for images (Intersection Observer), async loading of slider scripts, and WebP conversion. As a result, even with 10 banners on a page, LCP remains below 2 seconds under good network conditions. Additionally, we configure monitoring via Lighthouse CI — before deployment we check that a new banner does not increase INP. We guarantee stable performance metrics.
Implementation Process (e-commerce example)
- Audit of current ad slots (5-10 slots) — 2 hours.
- Database schema design considering page targeting and A/B testing — 1 day.
- REST API implementation with Redis caching — 2 days.
- Integration with React frontend via a custom useBanners hook — 1 day.
- Analytics setup for impressions and clicks with Google Analytics integration — 1 day.
- Performance testing with Lighthouse — 0.5 days.
- Deployment and marketer training — 2 hours.
Typical mistakes when developing a banner system:
- Incorrect cache configuration leads to outdated banners being shown.
- Lack of banner count limit causes page overload.
- Ignoring responsive images worsens mobile UX.
What is Included in the Work (Deliverables)
- Documentation: API description, instructions for marketers.
- Responsive layout: mobile and desktop versions of each banner.
- Caching: Redis or file cache configuration for high performance.
- Analytics: impressions and clicks database, automatic reports.
- Trial period: 2 weeks of free support after launch.
- Training: 2-hour call for the marketing team.
Development timeline: from 2 days to 2 weeks depending on complexity. Cost is calculated individually — discuss your task with our engineer. Get a consultation: we will analyze your current banners for free and propose optimization. This turnkey solution includes all deliverables: access to admin panel, editable banner zones, and performance SLAs.







