Fix LCP Issues on Textpattern (Loading Speed) | OpsBlu Docs

Fix LCP Issues on Textpattern (Loading Speed)

Improve Textpattern LCP by enabling page-level caching, optimizing image uploads via the Files tab, and streamlining Txp tag output.

General Guide: See Global LCP Guide for universal concepts and fixes.

What is LCP?

Largest Contentful Paint measures when the largest content element becomes visible. Google recommends LCP under 2.5 seconds. Textpattern is a lightweight PHP CMS that uses its own tag-based template language (Txp tags), MySQL for content storage, and a flat theme structure. LCP depends on MySQL query efficiency, image handling in the Files/Images panel, and whether any caching layer is in place.

Textpattern-Specific LCP Causes

  • No built-in page caching -- Textpattern processes Txp tags and queries MySQL on every request without any output cache
  • Unoptimized uploaded images -- images uploaded via the Images tab serve at original resolution with no server-side processing
  • Complex Txp tag nesting -- deeply nested <txp:article>, <txp:if_section>, and custom form calls add PHP/MySQL overhead
  • Plugin overhead -- active plugins (smd_thumbnail, rah_cache, etc.) hook into every page render
  • Theme CSS loaded synchronously -- theme stylesheets block rendering until fully downloaded

Fixes

1. Install a Caching Plugin

Use rah_cache or implement server-level caching:

# Install rah_cache plugin via Textpattern Admin > Plugins
# Upload rah_cache_v0.4.txt and activate

# Alternative: .htaccess level caching
# Create cached HTML files and serve them directly

Or add manual output caching in your page template:

<!-- In your Textpattern Page template -->
<txp:php>
$cacheDir = txpath . '/cache/';
$cacheFile = $cacheDir . md5($_SERVER['REQUEST_URI']) . '.html';
$cacheTTL = 3600;

if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTTL)) {
    readfile($cacheFile);
    exit;
}
ob_start();
</txp:php>

<!-- ... rest of your page template ... -->

<txp:php>
$output = ob_get_contents();
if (!is_dir($cacheDir)) mkdir($cacheDir, 0755, true);
file_put_contents($cacheFile, $output);
</txp:php>

2. Optimize Image Output

Use smd_thumbnail plugin for responsive images, or manually size images in templates:

<!-- Using Textpattern's built-in image tag with dimensions -->
<txp:images category="hero" limit="1">
  <img
    src="<txp:image_url />"
    width="<txp:image_info type="w" />"
    height="<txp:image_info type="h" />"
    alt="<txp:image_info type="alt" />"
    loading="eager"
    fetchpriority="high"
    style="aspect-ratio: <txp:image_info type='w' />/<txp:image_info type='h' />; width: 100%; height: auto; object-fit: cover;"
  >
</txp:images>

Pre-optimize images before uploading:

# Compress images before uploading to Textpattern
mogrify -strip -quality 80 -resize "1920>" *.jpg
optipng -o2 *.png
cwebp -q 80 hero.jpg -o hero.webp

3. Reduce Txp Tag Complexity

<!-- SLOW: Nested article calls with multiple form includes -->
<txp:article_custom section="blog" limit="10" form="blog_list">
  <!-- blog_list form calls sub-forms for image, author, tags -->
</txp:article_custom>

<!-- FASTER: Inline the output directly -->
<txp:article_custom section="blog" limit="6">
  <div class="blog-card">
    <txp:if_article_image>
      <txp:article_image width="400" height="225" />
    </txp:if_article_image>
    <h3><txp:permlink><txp:title /></txp:permlink></h3>
    <p><txp:excerpt /></p>
  </div>
</txp:article_custom>

4. Inline Critical CSS

<!-- In your Textpattern Page template <head> -->
<head>
  <style>
    /* Critical above-fold CSS */
    body { font-family: system-ui, sans-serif; margin: 0; }
    .header { display: flex; height: 64px; align-items: center; }
    .hero { width: 100%; aspect-ratio: 16/9; }
    .content { max-width: 800px; margin: 0 auto; padding: 1rem; }
  </style>

  <!-- Defer full theme CSS -->
  <link rel="preload" href="<txp:site_url />css/style.css" as="style"
  <noscript>
    <link rel="stylesheet" href="<txp:site_url />css/style.css">
  </noscript>
</head>

5. Add Server-Level Caching

# .htaccess
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
</IfModule>

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/css application/javascript
</IfModule>

Measuring LCP on Textpattern

  1. Textpattern admin > Diagnostics -- check for plugin conflicts and PHP version
  2. PageSpeed Insights -- test homepage and article listing pages
  3. TTFB check -- curl -w "TTFB: %{time_starttransfer}\n" -o /dev/null -s https://yoursite.com -- if over 500ms, install a caching plugin
  4. Plugin audit -- deactivate plugins one by one to measure their individual LCP impact

Analytics Script Impact

  • Textpattern has no built-in analytics -- all tracking is manually added to page templates
  • Place <script async> tags at the bottom of your page template before </body>
  • Use Textpattern's <txp:if_section> to conditionally load analytics only on public sections
  • Avoid synchronous document.write() analytics snippets