WordPress

Tutoriel : Créer un site de petites annonces avec WordPress

16 min de lecture

Créer un site de petites annonces avec WordPress : guide technique complet

Un site de petites annonces est l’un des projets WordPress les plus rentables en Afrique de l’Ouest. Que ce soit pour l’immobilier à Dakar, les véhicules d’occasion, l’emploi ou les services, le modèle fonctionne parce que les gens cherchent activement ces plateformes. Ce guide vous montre comment construire un site type Expat-Dakar ou CoinAfrique avec WordPress, étape par étape.

1. Choisir le bon plugin de petites annonces

Plugin Prix Points forts Limites
ClassiPress (thème AppThemes) ~69 $ Solution tout-en-un, paiements intégrés, système de prix par catégorie Design daté, mises à jour lentes
AdForest Gratuit + Pro 149 $/an Moderne, compatible Elementor, système de paquets d’annonces Version gratuite limitée
AWP Classifieds Gratuit + modules payants Léger, modulaire, bon pour démarrer Moins de fonctionnalités natives
Solution Custom (CPT + ACF) Gratuit Contrôle total, performance optimale Requiert du développement

Recommandation pour le Sénégal : la solution custom avec CPT + ACF Pro vous donne le contrôle total et les meilleures performances sur les connexions mobiles. C’est ce que nous allons construire.

2. Créer le Custom Post Type « Annonce »

Ajoutez dans functions.php de votre thème enfant :

// Enregistrer le CPT Annonce
add_action('init', function() {
    register_post_type('annonce', [
        'labels' => [
            'name' => 'Annonces',
            'singular_name' => 'Annonce',
            'add_new' => 'Publier une annonce',
            'add_new_item' => 'Nouvelle annonce',
            'edit_item' => 'Modifier l\'annonce',
            'view_item' => 'Voir l\'annonce',
            'search_items' => 'Rechercher une annonce',
        ],
        'public' => true,
        'has_archive' => true,
        'rewrite' => ['slug' => 'annonces'],
        'supports' => ['title', 'editor', 'thumbnail', 'author'],
        'menu_icon' => 'dashicons-megaphone',
        'show_in_rest' => true,
    ]);

    // Taxonomie : Catégorie d'annonce
    register_taxonomy('categorie_annonce', 'annonce', [
        'labels' => [
            'name' => 'Catégories',
            'singular_name' => 'Catégorie',
        ],
        'hierarchical' => true,
        'rewrite' => ['slug' => 'categorie'],
        'show_in_rest' => true,
    ]);

    // Taxonomie : Localisation
    register_taxonomy('localisation', 'annonce', [
        'labels' => [
            'name' => 'Localisations',
            'singular_name' => 'Localisation',
        ],
        'hierarchical' => true,
        'rewrite' => ['slug' => 'lieu'],
        'show_in_rest' => true,
    ]);
});

// Catégories par défaut
add_action('init', function() {
    $categories = [
        'Immobilier' => ['Appartements', 'Maisons', 'Terrains', 'Bureaux'],
        'Véhicules' => ['Voitures', 'Motos', 'Pièces détachées'],
        'Emploi' => ['Offres d\'emploi', 'Demandes d\'emploi', 'Stages'],
        'Électronique' => ['Téléphones', 'Ordinateurs', 'TV & Audio'],
        'Services' => ['Cours particuliers', 'Artisans', 'Transport'],
    ];
    
    foreach ($categories as $parent => $children) {
        if (!term_exists($parent, 'categorie_annonce')) {
            $term = wp_insert_term($parent, 'categorie_annonce');
            $parent_id = $term['term_id'];
            foreach ($children as $child) {
                if (!term_exists($child, 'categorie_annonce')) {
                    wp_insert_term($child, 'categorie_annonce', 
                      ['parent' => $parent_id]);
                }
            }
        }
    }
    
    // Localisations Sénégal
    $villes = ['Dakar' => ['Plateau', 'Almadies', 'Parcelles Assainies', 
      'Guédiawaye', 'Pikine', 'Rufisque'], 'Thiès' => [], 
      'Saint-Louis' => [], 'Ziguinchor' => [], 'Kaolack' => []];
    
    foreach ($villes as $ville => $quartiers) {
        if (!term_exists($ville, 'localisation')) {
            $term = wp_insert_term($ville, 'localisation');
            foreach ($quartiers as $q) {
                if (!term_exists($q, 'localisation')) {
                    wp_insert_term($q, 'localisation', 
                      ['parent' => $term['term_id']]);
                }
            }
        }
    }
}, 20);

3. Champs personnalisés avec ACF

Installez ACF (Advanced Custom Fields) et créez ces champs pour le CPT « annonce » :

Champ Type ACF Options
Prix Number Suffixe : « FCFA », minimum : 0
Type d’annonce Select Vente, Location, Don, Échange
État Select Neuf, Occasion – Bon état, Occasion – État moyen
Téléphone Text Placeholder : « +221 7X XXX XX XX »
WhatsApp True/False Joignable sur WhatsApp
Galerie photos Gallery Max 8 images, taille max 2 Mo
Urgent True/False Annonce urgente (mise en avant)

4. Template d’affichage des annonces

CSS pour les cartes d’annonces

/* Grille d'annonces responsive */
.annonces-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
  padding: 20px 0;
}

.annonce-card {
  background: #fff;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.08);
  transition: transform 0.2s, box-shadow 0.2s;
  position: relative;
}

.annonce-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 8px 25px rgba(0,0,0,0.12);
}

.annonce-card__image {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.annonce-card__badge {
  position: absolute;
  top: 12px;
  left: 12px;
  background: #e94560;
  color: #fff;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}

.annonce-card__badge--urgent {
  background: #ff6b35;
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}

.annonce-card__body {
  padding: 15px;
}

.annonce-card__title {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 8px;
  color: #1a1a2e;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.annonce-card__price {
  font-size: 20px;
  font-weight: 700;
  color: #e94560;
  margin: 0 0 8px;
}

.annonce-card__meta {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: #666;
}

.annonce-card__location::before {
  content: "📍 ";
}

.annonce-card__whatsapp {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: #25D366;
  color: #fff;
  padding: 10px 20px;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 600;
  width: 100%;
  justify-content: center;
  margin-top: 12px;
}

/* Mobile : 1 colonne */
@media (max-width: 480px) {
  .annonces-grid {
    grid-template-columns: 1fr;
    gap: 15px;
    padding: 10px;
  }
  .annonce-card__image { height: 180px; }
}

Template PHP (archive-annonce.php)

<?php get_header(); ?>

<div class="annonces-container" style="max-width:1200px; margin:0 auto; padding:20px;">
  <h1>Petites Annonces</h1>
  
  <!-- Barre de recherche -->
  <form class="annonces-search" method="GET" action="<?php echo get_post_type_archive_link('annonce'); ?>">
    <input type="text" name="s" placeholder="Que recherchez-vous ?" 
      value="<?php echo esc_attr(get_search_query()); ?>"
      style="flex:1; padding:12px; border:2px solid #ddd; border-radius:8px;">
    <input type="hidden" name="post_type" value="annonce">
    
    <?php wp_dropdown_categories([
      'taxonomy' => 'categorie_annonce',
      'name' => 'categorie',
      'show_option_all' => 'Toutes les catégories',
      'hide_empty' => false,
    ]); ?>
    
    <?php wp_dropdown_categories([
      'taxonomy' => 'localisation',
      'name' => 'lieu',
      'show_option_all' => 'Toute localisation',
      'hide_empty' => false,
    ]); ?>
    
    <button type="submit">Rechercher</button>
  </form>

  <div class="annonces-grid">
    <?php while (have_posts()) : the_post(); 
      $prix = get_field('prix');
      $type = get_field('type_annonce');
      $urgent = get_field('urgent');
      $location = wp_get_post_terms(get_the_ID(), 'localisation');
      $whatsapp = get_field('whatsapp');
      $telephone = get_field('telephone');
    ?>
    <article class="annonce-card">
      <?php if ($urgent) : ?>
        <span class="annonce-card__badge annonce-card__badge--urgent">URGENT</span>
      <?php elseif ($type) : ?>
        <span class="annonce-card__badge"><?php echo esc_html($type); ?></span>
      <?php endif; ?>
      
      <?php if (has_post_thumbnail()) : ?>
        <img class="annonce-card__image" src="<?php the_post_thumbnail_url('medium_large'); ?>" 
          alt="<?php the_title_attribute(); ?>" loading="lazy">
      <?php endif; ?>
      
      <div class="annonce-card__body">
        <h3 class="annonce-card__title">
          <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
        </h3>
        <?php if ($prix) : ?>
          <p class="annonce-card__price"><?php echo number_format($prix, 0, ',', ' '); ?> FCFA</p>
        <?php endif; ?>
        <div class="annonce-card__meta">
          <span class="annonce-card__location">
            <?php echo $location ? esc_html($location[0]->name) : 'Sénégal'; ?>
          </span>
          <span><?php echo human_time_diff(get_the_time('U'), current_time('timestamp')) . ' ago'; ?></span>
        </div>
        <?php if ($whatsapp && $telephone) : ?>
          <a class="annonce-card__whatsapp" 
            href="https://wa.me/<?php echo preg_replace('/[^0-9]/', '', $telephone); ?>?text=Bonjour, je suis intéressé par : <?php echo urlencode(get_the_title()); ?>">
            Contacter via WhatsApp
          </a>
        <?php endif; ?>
      </div>
    </article>
    <?php endwhile; ?>
  </div>
  
  <?php the_posts_pagination(['mid_size' => 2]); ?>
</div>

<?php get_footer(); ?>

5. Soumission d’annonces en front-end

Permettez aux utilisateurs de publier des annonces sans accéder au back-office :

// Shortcode [formulaire_annonce]
add_shortcode('formulaire_annonce', function() {
    if (!is_user_logged_in()) {
        return '<div class="alert">
          <a href="' . wp_login_url(get_permalink()) . '">Connectez-vous</a> 
          pour publier une annonce.</div>';
    }
    
    // Traitement du formulaire
    if (isset($_POST['annonce_submit']) && wp_verify_nonce($_POST['annonce_nonce'], 'publier_annonce')) {
        $post_id = wp_insert_post([
            'post_title' => sanitize_text_field($_POST['titre']),
            'post_content' => wp_kses_post($_POST['description']),
            'post_type' => 'annonce',
            'post_status' => 'pending', // Modération avant publication
            'post_author' => get_current_user_id(),
        ]);
        
        if ($post_id) {
            update_field('prix', intval($_POST['prix']), $post_id);
            update_field('type_annonce', sanitize_text_field($_POST['type']), $post_id);
            update_field('telephone', sanitize_text_field($_POST['telephone']), $post_id);
            update_field('whatsapp', isset($_POST['whatsapp']), $post_id);
            
            // Gérer l'upload de la photo
            if (!empty($_FILES['photo']['name'])) {
                require_once ABSPATH . 'wp-admin/includes/image.php';
                require_once ABSPATH . 'wp-admin/includes/file.php';
                require_once ABSPATH . 'wp-admin/includes/media.php';
                $attach_id = media_handle_upload('photo', $post_id);
                if (!is_wp_error($attach_id)) {
                    set_post_thumbnail($post_id, $attach_id);
                }
            }
            
            wp_set_object_terms($post_id, intval($_POST['categorie']), 'categorie_annonce');
            wp_set_object_terms($post_id, intval($_POST['localisation']), 'localisation');
            
            return '<div class="alert-success">Annonce soumise ! 
              Elle sera visible après validation par notre équipe.</div>';
        }
    }
    
    ob_start(); ?>
    <form method="post" enctype="multipart/form-data" class="form-annonce">
      <?php wp_nonce_field('publier_annonce', 'annonce_nonce'); ?>
      
      <div class="form-group">
        <label>Titre de l'annonce *</label>
        <input type="text" name="titre" required maxlength="100" 
          placeholder="Ex: iPhone 13 Pro 128Go">
      </div>
      
      <div class="form-row">
        <div class="form-group">
          <label>Prix (FCFA) *</label>
          <input type="number" name="prix" required min="0" 
            placeholder="Ex: 350000">
        </div>
        <div class="form-group">
          <label>Type</label>
          <select name="type">
            <option value="Vente">Vente</option>
            <option value="Location">Location</option>
            <option value="Don">Don</option>
          </select>
        </div>
      </div>
      
      <div class="form-group">
        <label>Description *</label>
        <textarea name="description" required rows="5" 
          placeholder="Décrivez votre article en détail..."></textarea>
      </div>
      
      <div class="form-group">
        <label>Photo principale</label>
        <input type="file" name="photo" accept="image/*">
        <small>Max 2 Mo. Formats : JPG, PNG, WebP</small>
      </div>
      
      <div class="form-row">
        <div class="form-group">
          <label>Téléphone *</label>
          <input type="tel" name="telephone" required placeholder="+221 7X XXX XX XX">
        </div>
        <div class="form-group">
          <label><input type="checkbox" name="whatsapp"> Joignable sur WhatsApp</label>
        </div>
      </div>
      
      <button type="submit" name="annonce_submit" class="btn-submit">
        Publier mon annonce
      </button>
    </form>
    <?php return ob_get_clean();
});

6. Monétisation du site d’annonces

Les modèles économiques qui fonctionnent au Sénégal :

Annonces premium payantes

// Système de mise en avant payante via PayDunya
add_action('wp_ajax_promouvoir_annonce', function() {
    $post_id = intval($_POST['post_id']);
    $duree = intval($_POST['duree']); // jours
    
    $tarifs = [
        7 => 2000,   // 2 000 FCFA / semaine
        30 => 5000,  // 5 000 FCFA / mois
        90 => 12000, // 12 000 FCFA / trimestre
    ];
    
    $montant = $tarifs[$duree] ?? $tarifs[7];
    
    // Créer la facture PayDunya
    $invoice = [
        'amount' => $montant,
        'description' => 'Mise en avant annonce #' . $post_id . ' - ' . $duree . ' jours',
        'callback_url' => home_url('/paydunya-callback/'),
        'return_url' => get_permalink($post_id),
        'custom_data' => ['post_id' => $post_id, 'duree' => $duree],
    ];
    
    // Après paiement confirmé via callback :
    // update_post_meta($post_id, 'annonce_premium', true);
    // update_post_meta($post_id, 'premium_expiry', date('Y-m-d', strtotime("+$duree days")));
});

Modèle de revenus recommandé

  • Annonces gratuites : publication basique, visibilité normale
  • Annonce Urgente : 1 000 FCFA — badge « URGENT » + mise en avant 3 jours
  • Annonce Premium : 2 000 FCFA/semaine — affichée en tête des résultats
  • Pack Pro : 15 000 FCFA/mois — 10 annonces premium + logo vendeur
  • Bannières publicitaires : vendez des espaces aux entreprises locales

7. Optimisations pour le marché sénégalais

  • WhatsApp partout : le bouton WhatsApp est le principal canal de contact. Intégrez-le sur chaque annonce
  • Images compressées : limitez les uploads à 2 Mo et convertissez en WebP automatiquement (plugin ShortPixel ou Imagify)
  • Lazy loading : chargez les images uniquement quand elles apparaissent à l’écran (attribut loading="lazy")
  • Cache agressif : les pages de listing changent souvent, mais les pages d’annonces individuelles peuvent être mises en cache 1h
  • SMS de notification : utilisez l’API Orange SMS pour notifier les vendeurs quand quelqu’un contacte via le formulaire
  • Paiement mobile : intégrez Wave et Orange Money pour les annonces premium (via PayDunya ou PayTech)

8. SEO pour les petites annonces

// Schema.org pour chaque annonce
add_action('wp_head', function() {
    if (is_singular('annonce')) {
        $prix = get_field('prix');
        $location = wp_get_post_terms(get_the_ID(), 'localisation');
        $schema = [
            '@context' => 'https://schema.org',
            '@type' => 'Product',
            'name' => get_the_title(),
            'description' => wp_strip_all_tags(get_the_excerpt()),
            'image' => get_the_post_thumbnail_url(get_the_ID(), 'large'),
            'offers' => [
                '@type' => 'Offer',
                'price' => $prix ?: 0,
                'priceCurrency' => 'XOF',
                'availability' => 'https://schema.org/InStock',
                'areaServed' => $location ? $location[0]->name : 'Dakar, Sénégal',
            ],
        ];
        echo '<script type="application/ld+json">' . 
          json_encode($schema, JSON_UNESCAPED_UNICODE) . '</script>';
    }
});

Avec cette architecture, vous avez un site de petites annonces complet, monétisable et adapté au marché sénégalais. Le front-end est mobile-first, les paiements passent par le mobile money, et WhatsApp sert de canal de communication principal entre acheteurs et vendeurs.

Pour ne pas tout faire vous-même

Notre équipe conçoit votre site, met en place domaine et hébergement, vous forme à la gestion, et reste joignable 6 mois pour les questions techniques.

À partir de 350 000 FCFA

📧 E-mail
💬 WhatsApp

Architecture d un site de petites annonces avec WordPress

Le pattern technique pour un site de petites annonces (immobilier, automobile, emploi, services) repose sur 4 piliers : un Custom Post Type Annonce avec champs personnalises, une recherche avancee avec filtres, un systeme de gestion d annonceurs (compte vendeur), et eventuellement un module de paiement pour les annonces premium. WordPress avec le bon stack couvre ces 4 piliers sans developpement custom lourd.

Les approches en 2026 : plugin clé-en-main (AWP Classifieds, AdForest, Classified Listing) pour les projets standardises avec budget contraint ; theme dedie (HivePress, Marketo, MyListing) qui livre une experience complete prête-a-l emploi ; stack custom ACF + Search&Filter Pro pour les projets avec besoins specifiques. Le choix se fait sur le niveau de personnalisation requis.

Plugin HivePress — l ecosysteme dominant

HivePress (hivepress.io) est devenu en 2024-2026 la reference open-source pour les sites de marketplace et petites annonces sur WordPress. Le coeur est gratuit, des extensions premium (paiement Stripe, geolocalisation, messagerie, plans tarifaires) sont disponibles individuellement (40-100 USD/extension).

Fonctionnalites natives : compte vendeur avec dashboard personnel, soumission d annonce depuis le frontend, modele de categories et attributs personnalisables, recherche par localisation, messagerie interne entre vendeurs et acheteurs. Le tout pre-style et adaptable a un theme via les hooks WordPress standards.

Custom Post Type — le pattern de base

Pour un projet custom (sans plugin lourd), enregistrer un CPT Annonce avec ACF Pro pour les champs : prix, type (vente/location/echange), localisation, photos (gallery), caracteristiques specifiques au secteur (m2, nombre de pieces pour immobilier ; marque, modele, kilometrage pour automobile). Une taxonomie Categorie hierarchique (Immobilier → Vente → Maison ; Immobilier → Location → Appartement) et une taxonomie Ville pour le filtrage geographique.

Un CPT correctement structure en 2-3 jours de developpement supporte 10 000-100 000 annonces sans probleme de performance, a condition d ajouter les bons indexes MySQL sur les meta_keys utilisees en recherche (prix, ville, categorie).

Recherche et filtres avancees

Le coeur de l experience utilisateur d un site d annonces est la recherche. Trois patterns :

WP Native Search Form avec query custom dans pre_get_posts. Suffisant pour 1000-10000 annonces. Limite : pas de facettes interactives (filtres a coches).

Search and Filter Pro (plugin payant 89 USD/an) ou Facetwp (149 USD/an) ajoutent facets, AJAX, pagination. Travaillent avec les CPT et taxonomies WordPress sans modification du backend. Couvre la majorite des projets.

ElasticPress + Elasticsearch pour les sites a fort volume (100 000+ annonces) ou avec recherche complexe (geolocalisation, full-text avance, autocompletion). Necessite un serveur Elasticsearch en sus du serveur WordPress (15-50 EUR/mois). Reservatif aux projets matures.

Monetisation des annonces

Quatre modeles dominants en 2026.

Abonnement vendeur : 5-50 EUR/mois selon le nombre d annonces et la visibilite. Modele predictif, fideilse les annonceurs serieux. Adapte aux pros (agences immobilieres, concessionnaires auto).

Paiement a l annonce : 2-10 EUR par annonce publiee, gratuit pour les particuliers. Modele Leboncoin historique. Bonne conversion sur les annonceurs occasionnels.

Annonce premium / mise en avant : prix de base + 5-20 EUR pour faire remonter en haut de liste, surligner en couleur, ou inclure dans la newsletter. Booster a fort taux de conversion.

Commission sur transaction : 5-20 pour cent du montant si la transaction se conclut via la plateforme. Pour cela, integrer un systeme de paiement entre acheteur et vendeur (Stripe Connect par exemple). Modele Airbnb, Vinted. Plus complexe a mettre en place.

Moderation et qualite

Un site d annonces sans moderation se remplit de spam et perd toute valeur en quelques semaines. Trois niveaux.

Moderation automatique : detection de mots interdits (langage injurieux, illegaux, escroquerie), verification que les photos ne sont pas reverse-image-search des photos prises sur d autres sites (signe de fraude). Tools : OpenAI Moderation API (gratuit pour le contenu textuel), Google Cloud Vision pour l image.

Moderation a posteriori : une annonce est publiee immediatement mais signalee aux moderateurs si elle declenche une regle de risque (nouveau compte + prix tres bas + categorie sensible). Equilibre rapidite de publication et qualite.

Signalement utilisateur : bouton Signaler sur chaque annonce. 3+ signalements declenchent une moderation manuelle. Multiplie l effort des moderateurs par la communaute.

FAQ

WordPress est-il vraiment adapte a un site d annonces ?
Oui jusqu a 50 000-100 000 annonces actives. Au-dela, une architecture custom (Laravel, Django, Rails) ou un service specialise devient pertinent pour la performance et le scaling. Pour 99 pour cent des projets PME, WordPress suffit.

Combien coute le developpement complet ?
Avec HivePress et un theme premium : 2 000-5 000 EUR pour un site fonctionnel. Avec stack custom et UX raffinee : 8 000-25 000 EUR. Solution agence cle en main : 15 000-50 000 EUR. Le facteur principal : niveau de personnalisation du flow et du design.

Comment respecter le RGPD pour les annonces ?
(1) Consentement explicite a la creation de compte. (2) Politique de confidentialite detaillee. (3) Bouton de suppression de compte qui supprime egalement les annonces. (4) Anonymisation des annonces archivees apres X mois. (5) Pas de partage de donnees a des tiers sans consentement.

Faut-il un app mobile en complement ?
Pas obligatoire. Un site responsive bien fait avec PWA (Progressive Web App, ajoute installable au home screen) couvre 80 pour cent des cas. Une app mobile native devient pertinente pour les volumes massifs (500 000+ utilisateurs actifs) ou les workflows critiques (notifications push de nouvelles annonces correspondant a un alert).

References

Partager