DEV Community

ahmet gedik
ahmet gedik

Posted on

Server-Side Rendering Strategies for Video Platforms

In the era of React and Next.js, I chose full server-side rendering with PHP for TopVideoHub. Here's why, and how I implemented it for optimal performance.

Why SSR with PHP (Not a JS Framework)

For a content-heavy video platform, SSR with PHP has clear advantages:

  1. SEO — Search engines see fully rendered HTML immediately. No hydration delay, no JS-dependent content.
  2. Performance — Zero JavaScript framework overhead. The page is ready on first paint.
  3. Hosting cost — PHP shared hosting costs $3-10/month. Node.js hosting starts at $5-20/month for comparable performance.
  4. Simplicity — No build step, no bundler configuration, no client/server code splitting.

Template Architecture

I use plain PHP templates with a simple layout system:

// app/View.php
class View {
    private string $layout = 'main';
    private array $data = [];
    private array $sections = [];

    public function render(string $template, array $data = []): string {
        $this->data = $data;
        extract($data);

        ob_start();
        require __DIR__ . "/../templates/{$template}.php";
        $content = ob_get_clean();

        $this->sections['content'] = $content;

        ob_start();
        require __DIR__ . "/../templates/layouts/{$this->layout}.php";
        return ob_get_clean();
    }

    public function section(string $name): string {
        return $this->sections[$name] ?? '';
    }

    public function setLayout(string $layout): void {
        $this->layout = $layout;
    }
}
Enter fullscreen mode Exit fullscreen mode
<!-- templates/layouts/main.php -->
<!DOCTYPE html>
<html lang="<?= $lang ?? 'en' ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= htmlspecialchars($title ?? 'TopVideoHub') ?></title>
    <?= $this->section('head') ?>
    <link rel="stylesheet" href="/assets/style.css">
</head>
<body>
    <?php require __DIR__ . '/../partials/nav.php'; ?>
    <main>
        <?= $this->section('content') ?>
    </main>
    <?php require __DIR__ . '/../partials/footer.php'; ?>
    <?= $this->section('scripts') ?>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Video Grid Component

<!-- templates/partials/video-grid.php -->
<div class="video-grid" data-region="<?= $region ?>">
    <?php foreach ($videos as $video): ?>
    <article class="video-card" data-video-id="<?= $video['video_id'] ?>">
        <div class="video-facade">
            <img src="<?= thumbnailUrl($video['video_id'], 'mq') ?>"
                 alt="<?= htmlspecialchars($video['title']) ?>"
                 width="320" height="180"
                 loading="<?= $loop_index < 4 ? 'eager' : 'lazy' ?>"
                 decoding="async">
            <button class="play-btn" aria-label="Play <?= htmlspecialchars($video['title']) ?>">
                <svg viewBox="0 0 68 48"><!-- play icon SVG --></svg>
            </button>
            <span class="video-duration">
                <?= formatDuration($video['duration']) ?>
            </span>
        </div>
        <div class="video-info">
            <h3 class="video-title">
                <a href="/watch/<?= $video['video_id'] ?>">
                    <?= htmlspecialchars($video['title']) ?>
                </a>
            </h3>
            <p class="video-channel">
                <?= htmlspecialchars($video['channel_title']) ?>
            </p>
            <p class="video-meta">
                <span><?= formatViewCount($video['view_count']) ?> views</span>
                <span><?= timeAgo($video['published_at']) ?></span>
            </p>
        </div>
    </article>
    <?php endforeach; ?>
</div>
Enter fullscreen mode Exit fullscreen mode

Progressive Enhancement

The page works without JavaScript. JS enhances the experience:

// Progressive enhancement: only runs if JS is available
(function() {
    // Lazy-load video embeds on click
    document.querySelectorAll('.video-facade').forEach(facade => {
        facade.addEventListener('click', function() {
            const id = this.closest('.video-card').dataset.videoId;
            const iframe = document.createElement('iframe');
            iframe.src = `https://www.youtube.com/embed/${id}?autoplay=1`;
            iframe.allow = 'autoplay; encrypted-media';
            iframe.allowFullscreen = true;
            this.innerHTML = '';
            this.appendChild(iframe);
        });
    });

    // Catbar responsive sizing
    const catbar = document.querySelector('.vw-catbar');
    if (catbar) {
        fitCatbar(catbar);
        window.addEventListener('resize', () => fitCatbar(catbar));
    }
})();
Enter fullscreen mode Exit fullscreen mode

Cache-Aware Rendering

Since LiteSpeed caches full HTML responses, I ensure the rendered output is cache-friendly:

class CacheAwareRenderer {
    public function render(string $template, array $data, PageType $type): string {
        // Set cache headers before any output
        CachePolicy::apply($type, ...($data['cache_tags'] ?? []));

        // Render template
        $view = new View();
        $html = $view->render($template, $data);

        // Also write to PHP file cache as fallback
        $cacheKey = md5($_SERVER['REQUEST_URI']);
        $cachePath = __DIR__ . "/../data/pagecache/{$cacheKey}.html";
        file_put_contents($cachePath, $html, LOCK_EX);

        return $html;
    }
}
Enter fullscreen mode Exit fullscreen mode

Results at TopVideoHub

This SSR approach on TopVideoHub delivers:

  • TTFB: ~12ms (cached), ~120ms (uncached)
  • FCP: ~0.8s
  • Total JS: 8KB (minified)
  • Lighthouse Performance: 85+ mobile, 95+ desktop

No build step, no hydration, no JavaScript framework. Just PHP rendering HTML and LiteSpeed caching it. For content platforms, this approach is hard to beat.

Top comments (0)