Pourquoi j’ai arrêté les Page Builders WordPress

anthowd anthowd
wp-les-builders-customwp-anthowd

(et comment mes clients m’en remercient)

Développer des thèmes WordPress custom sans page builder offre un contrôle total sur le code, réduit la lourdeur et les conflits, et améliore les performances et la maintenabilité à long terme. Cette approche évite le lock-in, les scripts inutiles et les temps de chargement excessifs, pour un site taillé sur mesure qui évolue avec les besoins du client.

L’appel d’urgence qui a tout changé

Un vendredi soir, alors que je terminais ma semaine tranquillement, j’ai reçu l’appel redouté : un client dont le site vitrine était complètement inutilisable. L’éditeur se figeait à chaque clic, les pages mettaient plus de dix secondes à charger, et pire encore, les modifications qu’il tentait de faire disparaissaient mystérieusement. En ouvrant le capot, le diagnostic était sans appel : un page builder surchargé générait des dizaines de requêtes inutiles, empilait des couches de CSS et JavaScript redondantes, et entrait en conflit avec au moins trois autres extensions essentielles au bon fonctionnement du site.

Le code que j’ai trouvé ressemblait à ça :

<!-- Code généré par le page builder -->
<div class="builder-wrapper builder-section-wrapper">
  <div class="builder-container builder-row">
    <div class="builder-col builder-col-12 builder-col-md-6">
      <div class="builder-element builder-widget-heading">
        <div class="builder-widget-container">
          <h2 class="builder-heading-title builder-size-default">
            Mon titre
          </h2>
        </div>
      </div>
    </div>
  </div>
</div>
<!-- + 15 fichiers CSS et 12 fichiers JS chargés -->
Langage du code : HTML, XML (xml)

C’était exactement comme conduire une Formule 1 en première vitesse sur l’autoroute : toute la puissance de WordPress était là, mais complètement bridée par une surcouche logicielle qui promettait la simplicité et livrait un cauchemar technique.

Mon constat après des années avec les page builders

Pourquoi les builders deviennent un fardeau technique

Avec l’expérience et la multiplication des projets, j’ai progressivement pris conscience que les page builders, loin de simplifier mon travail de développeur, rajoutaient systématiquement plus de problèmes qu’ils n’en résolvaient sur les sites nécessitant du sur-mesure. La lourdeur technique n’est pas un mythe ou une exagération de puristes : des tests réels montrent que des pages construites avec certains builders peuvent générer jusqu’à 50% de requêtes HTTP supplémentaires et un poids de page significativement plus élevé par rapport à un thème custom optimisé.

Le piège du lock-in et du code empilé

Le véritable piège, c’est ce qu’on appelle le lock-in technique : une fois qu’un site est construit avec un page builder, toute son architecture de contenu devient dépendante de cet outil. Regardez comment le contenu est stocké en base de données :

[builder_section][builder_row][builder_column width="1/2"]
[builder_heading title="Mon titre" size="h2"]
[builder_text]Mon contenu ici[/builder_text]
[/builder_column][/builder_row][/builder_section]
Langage du code : PHP (php)

Si demain le client souhaite migrer vers une solution plus performante, il se retrouve avec une base de données truffée de shortcodes propriétaires impossibles à nettoyer sans refonte complète.

Ma solution : le développement de thèmes custom

Comment je développe des thèmes WordPress performants

Face à ces freins récurrents qui empoisonnaient mes projets et déçevaient mes clients, j’ai adopté une approche radicalement différente : développer des thèmes WordPress entièrement custom en PHP pur, adossés à l’éditeur de blocs natif et au fichier theme.json pour piloter les styles et réglages globaux.

Exemple 1 : Un template propre et lisible

Au lieu de shortcodes imbriqués, voici à quoi ressemble un template custom :

<?php
/**
 * Template Name: Page d'accueil
 */
get_header(); ?>

<main class="home-content">
    <?php if (have_posts()) : while (have_posts()) : the_post(); ?>
        
        <section class="hero">
            <h1><?php the_title(); ?></h1>
            <?php the_content(); ?>
        </section>

        <?php 
        // Récupération des derniers articles
        $recent_posts = new WP_Query([
            'posts_per_page' => 3,
            'post_status' => 'publish'
        ]);
        ?>

        <?php if ($recent_posts->have_posts()) : ?>
            <section class="recent-posts">
                <h2>Derniers articles</h2>
                <div class="posts-grid">
                    <?php while ($recent_posts->have_posts()) : $recent_posts->the_post(); ?>
                        <article class="post-card">
                            <?php if (has_post_thumbnail()) : ?>
                                <?php the_post_thumbnail('medium'); ?>
                            <?php endif; ?>
                            <h3><?php the_title(); ?></h3>
                            <?php the_excerpt(); ?>
                            <a href="<?php the_permalink(); ?>">Lire la suite</a>
                        </article>
                    <?php endwhile; ?>
                </div>
            </section>
        <?php endif; wp_reset_postdata(); ?>

    <?php endwhile; endif; ?>
</main>

<?php get_footer(); ?>
Langage du code : PHP (php)

Résultat : Code lisible, maintenable, et qui ne charge que ce qui est nécessaire.

Exemple 2 : Configuration centralisée avec theme.json

Au lieu de paramètres dispersés dans quinze interfaces différentes, tout est centralisé :

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "settings": {
    "color": {
      "palette": [
        {
          "name": "Primary",
          "slug": "primary",
          "color": "#0066cc"
        },
        {
          "name": "Secondary",
          "slug": "secondary",
          "color": "#333333"
        }
      ]
    },
    "typography": {
      "fontSizes": [
        {
          "name": "Small",
          "slug": "small",
          "size": "14px"
        },
        {
          "name": "Medium",
          "slug": "medium",
          "size": "18px"
        },
        {
          "name": "Large",
          "slug": "large",
          "size": "24px"
        }
      ],
      "fontFamilies": [
        {
          "name": "System",
          "slug": "system",
          "fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
        }
      ]
    },
    "spacing": {
      "units": ["px", "em", "rem", "%"],
      "spacingSizes": [
        {
          "name": "Small",
          "slug": "small",
          "size": "1rem"
        },
        {
          "name": "Medium",
          "slug": "medium",
          "size": "2rem"
        }
      ]
    }
  }
}
Langage du code : JSON / JSON avec commentaires (json)

Exemple 3 : Optimisation des performances

Pour charger les ressources uniquement quand nécessaire :

<?php
// functions.php
function theme_custom_enqueue_scripts() {
    // CSS principal uniquement
    wp_enqueue_style(
        'theme-style',
        get_stylesheet_uri(),
        array(),
        wp_get_theme()->get('Version')
    );
    
    // Script JS uniquement sur les pages qui en ont besoin
    if (is_page('contact')) {
        wp_enqueue_script(
            'contact-form',
            get_template_directory_uri() . '/js/contact.js',
            array(),
            wp_get_theme()->get('Version'),
            true
        );
    }
}
add_action('wp_enqueue_scripts', 'theme_custom_enqueue_scripts');

// Nettoyage du header
function theme_cleanup_header() {
    remove_action('wp_head', 'wp_generator');
    remove_action('wp_head', 'wlwmanifest_link');
    remove_action('wp_head', 'rsd_link');
}
add_action('init', 'theme_cleanup_header');
Langage du code : PHP (php)

Les bénéfices concrets pour mes clients

Les bénéfices pour le client sont immédiats et mesurables. D’abord, la vitesse : un thème sur mesure ne charge que le strict nécessaire, pas de JavaScript ou CSS de builder qui traînent en fond. Dans la pratique, cela se traduit par des temps de chargement divisés par deux ou trois.

Comparaison réelle des performances

Avec page builder :

  • 47 requêtes HTTP
  • 2.8 MB de ressources
  • Temps de chargement : 4.2 secondes
  • Score Lighthouse : 62/100

Avec thème custom :

  • 12 requêtes HTTP
  • 380 KB de ressources
  • Temps de chargement : 1.1 secondes
  • Score Lighthouse : 98/100

Maintenance simplifiée et personnalisation totale

Exemple 4 : Intégration API personnalisée

Besoin d’afficher des données d’un CRM ? Avec un thème custom, c’est simple :

<?php
function get_crm_data($customer_id) {
    $cache_key = 'crm_data_' . $customer_id;
    $cached_data = get_transient($cache_key);
    
    if (false !== $cached_data) {
        return $cached_data;
    }
    
    $response = wp_remote_get(
        'https://api.moncrm.com/customers/' . $customer_id,
        [
            'headers' => [
                'Authorization' => 'Bearer ' . CRM_API_KEY
            ]
        ]
    );
    
    if (is_wp_error($response)) {
        return false;
    }
    
    $data = json_decode(wp_remote_retrieve_body($response), true);
    set_transient($cache_key, $data, HOUR_IN_SECONDS);
    
    return $data;
}

// Utilisation dans un template
$customer_data = get_crm_data(get_current_user_id());
if ($customer_data) {
    echo '<p>Dernier achat : ' . esc_html($customer_data['last_purchase']) . '</p>';
}
Langage du code : PHP (php)

Exemple 5 : Bloc Gutenberg personnalisé

Pour des besoins métier spécifiques, création d’un bloc sur-mesure :

// block.js
import { registerBlockType } from '@wordpress/blocks';
import { RichText } from '@wordpress/block-editor';

registerBlockType('montheme/temoignage', {
    title: 'Témoignage Client',
    icon: 'format-quote',
    category: 'common',
    attributes: {
        quote: { type: 'string' },
        author: { type: 'string' },
        company: { type: 'string' }
    },
    
    edit: ({ attributes, setAttributes }) => {
        return (
            <div className="testimonial-block">
                <RichText
                    tagName="blockquote"
                    value={attributes.quote}
                    onChange={(quote) => setAttributes({ quote })}
                    placeholder="Citation du client..."
                />
                <RichText
                    tagName="p"
                    value={attributes.author}
                    onChange={(author) => setAttributes({ author })}
                    placeholder="Nom du client"
                />
                <RichText
                    tagName="p"
                    value={attributes.company}
                    onChange={(company) => setAttributes({ company })}
                    placeholder="Entreprise"
                />
            </div>
        );
    },
    
    save: ({ attributes }) => {
        return (
            <div className="testimonial">
                <blockquote>{attributes.quote}</blockquote>
                <cite>
                    <strong>{attributes.author}</strong>
                    {attributes.company && `, ${attributes.company}`}
                </cite>
            </div>
        );
    }
});
Langage du code : JavaScript (javascript)

Flexibilité et évolutivité garanties

Exemple 6 : Custom Post Type évolutif

<?php
// Création d'un type de contenu personnalisé
function register_portfolio_post_type() {
    register_post_type('portfolio', [
        'labels' => [
            'name' => 'Portfolio',
            'singular_name' => 'Projet'
        ],
        'public' => true,
        'has_archive' => true,
        'supports' => ['title', 'editor', 'thumbnail'],
        'show_in_rest' => true, // Compatible Gutenberg
        'rewrite' => ['slug' => 'realisations']
    ]);
}
add_action('init', 'register_portfolio_post_type');

// Template pour afficher les projets
// single-portfolio.php
get_header(); ?>

<article class="portfolio-single">
    <h1><?php the_title(); ?></h1>
    
    <?php if (has_post_thumbnail()) : ?>
        <figure class="portfolio-featured">
            <?php the_post_thumbnail('large'); ?>
        </figure>
    <?php endif; ?>
    
    <div class="portfolio-content">
        <?php the_content(); ?>
    </div>
    
    <?php 
    // Champs personnalisés ACF
    if (function_exists('get_field')) :
        $client = get_field('client_name');
        $date = get_field('project_date');
        $url = get_field('project_url');
    ?>
        <aside class="portfolio-meta">
            <?php if ($client) : ?>
                <p><strong>Client :</strong> <?php echo esc_html($client); ?></p>
            <?php endif; ?>
            <?php if ($date) : ?>
                <p><strong>Date :</strong> <?php echo esc_html($date); ?></p>
            <?php endif; ?>
            <?php if ($url) : ?>
                <p><a href="<?php echo esc_url($url); ?>" target="_blank">Voir le projet</a></p>
            <?php endif; ?>
        </aside>
    <?php endif; ?>
</article>

<?php get_footer(); ?>
Langage du code : PHP (php)

La leçon de freelance : investir dans la qualité

Pourquoi les choix techniques impactent la relation client

Après des années à naviguer entre solutions rapides et projets sur mesure, une leçon s’impose avec une clarté implacable : les choix techniques faits au démarrage d’un projet ont un impact direct et durable sur la satisfaction client, la réputation du développeur et la pérennité du site.

Comment construire une relation de confiance durable

Cette transparence vis-à-vis du client renforce profondément la relation de confiance : expliquer pourquoi on refuse le builder à la mode et préférer une approche maîtrisée et sur-mesure, c’est montrer qu’on privilégie la qualité et la pérennité du projet plutôt que le gain immédiat ou la facilité apparente.

Mes livrables systématiques

Pour chaque projet, je fournis désormais :

  1. Documentation du code avec commentaires clairs
  2. Guide de maintenance pour les mises à jour
  3. Comparatif de performances avant/après
  4. Repository Git pour le versioning
  5. Formation client sur l’éditeur de blocs natif
<?php
/**
 * Exemple de documentation dans le code
 * 
 * Cette fonction récupère et affiche les articles liés
 * basés sur les catégories partagées.
 * 
 * @param int $post_id ID de l'article actuel
 * @param int $count Nombre d'articles à afficher (défaut: 3)
 * @return void
 */
function display_related_posts($post_id, $count = 3) {
    $categories = wp_get_post_categories($post_id);
    
    if (empty($categories)) {
        return;
    }
    
    $args = [
        'category__in' => $categories,
        'post__not_in' => [$post_id],
        'posts_per_page' => $count,
        'orderby' => 'rand'
    ];
    
    $related = new WP_Query($args);
    
    if ($related->have_posts()) : ?>
        <section class="related-posts">
            <h3>Articles similaires</h3>
            <div class="posts-grid">
                <?php while ($related->have_posts()) : $related->the_post(); ?>
                    <article>
                        <a href="<?php the_permalink(); ?>">
                            <?php the_post_thumbnail('thumbnail'); ?>
                            <h4><?php the_title(); ?></h4>
                        </a>
                    </article>
                <?php endwhile; ?>
            </div>
        </section>
    <?php endif;
    
    wp_reset_postdata();
}
Langage du code : PHP (php)

En conclusion

Un client qui comprend qu’il possède un site léger, sans dépendance à un éditeur propriétaire, capable d’absorber les évolutions futures sans refonte coûteuse, et qui bénéficie d’une vraie expertise technique devient naturellement un ambassadeur du travail fourni.

Articles similaires