Get started with 33% off your first certification using code: 33OFFNEW

Generate social share cards for WooCommerce products automatically

4 min read
Published on 3rd July 2026

When a WooCommerce product gets shared, the preview is whatever the platform scrapes: usually the bare product image on a white background, no price, no name, no branding. That is a wasted impression. A proper social card with the product photo, title and price reads as a deliberate piece of marketing and earns more clicks. This article generates one automatically for every product when it is saved, and serves it as the share image on the product page. No design tool, no manual export and it stays in sync when you change a product.

What we are building

For each product you want a 1200x630 card showing the product image, its name and its price, on a branded background. We build that card as HTML, render it to a PNG through an API when the product is saved, store the URL in product meta, and output it as the og:image. The render happens on save, so shoppers never wait for it.

A product card has a fairly standard shape, so rather than inventing a layout you can lift the structure from the product card template and adapt it to your brand. The same endpoint powers general Open Graph images if you later want cards for categories or campaigns too.

Step 1: Build the card from the product

Pull the product's title, price and main image, and drop them into an inline-styled card. WooCommerce gives you all of it through the WC_Product object.

function ai_product_card_html( WC_Product $product ): string {
    $title = esc_html( $product->get_name() );
    $price = wp_strip_all_tags( $product->get_price_html() );
    $image = wp_get_attachment_image_url( $product->get_image_id(), 'large' )
        ?: wc_placeholder_img_src( 'large' );
    $shop  = esc_html( get_bloginfo( 'name' ) );

    return <<<HTML
    <div style="width:1200px;height:630px;display:flex;font-family:Arial,sans-serif;
                background:#0f172a;color:#fff;">
      <div style="flex:0 0 520px;display:flex;align-items:center;justify-content:center;
                  background:#fff;">
        <img src="{$image}" style="max-width:440px;max-height:520px;object-fit:contain;" />
      </div>
      <div style="flex:1;padding:64px;display:flex;flex-direction:column;justify-content:center;">
        <div style="font-size:22px;letter-spacing:.1em;text-transform:uppercase;opacity:.7;">{$shop}</div>
        <h1 style="font-size:54px;line-height:1.1;margin:18px 0;">{$title}</h1>
        <div style="font-size:46px;font-weight:bold;color:#34d399;">{$price}</div>
      </div>
    </div>
    HTML;
}

The product image goes in as a normal <img> tag. The API renders the card in real Chromium, so the remote image loads exactly as it would in a browser.

Step 2: Render the card when a product is saved

Hook the WooCommerce save events. Both new and updated products run through the same handler, so a card is always current with the latest price and photo.

add_action( 'woocommerce_update_product', 'ai_generate_product_card' );
add_action( 'woocommerce_new_product', 'ai_generate_product_card' );

function ai_generate_product_card( int $product_id ): void {
    $product = wc_get_product( $product_id );
    if ( ! $product ) {
        return;
    }

    $response = wp_remote_post( 'https://app.html2img.com/api/html', [
        'headers' => [ 'X-API-Key' => HTML2IMG_API_KEY, 'Content-Type' => 'application/json' ],
        'timeout' => 25,
        'body'    => wp_json_encode( [
            'html'   => ai_product_card_html( $product ),
            'width'  => 1200,
            'height' => 630,
        ] ),
    ] );

    if ( is_wp_error( $response ) ) {
        return;
    }

    $url = json_decode( wp_remote_retrieve_body( $response ), true )['url'] ?? null;
    if ( $url ) {
        update_post_meta( $product_id, '_social_card', esc_url_raw( $url ) );
    }
}

Because the price is baked into the image, regenerating on every save matters. A card showing last month's price on a discounted product is worse than no card at all, and hooking the save event keeps them honest.

Step 3: Serve the card on the product page

Output the stored card as the Open Graph and Twitter image on single product pages. Fall back to the product image only if a card has not been generated yet.

add_action( 'wp_head', function () {
    if ( ! is_product() ) {
        return;
    }

    $product_id = get_queried_object_id();
    $card = get_post_meta( $product_id, '_social_card', true );

    if ( ! $card ) {
        return;
    }

    printf( '<meta property="og:image" content="%s" />' . "\n", esc_url( $card ) );
    printf( '<meta property="og:image:width" content="1200" />' . "\n" );
    printf( '<meta property="og:image:height" content="630" />' . "\n" );
    printf( '<meta name="twitter:card" content="summary_large_image" />' . "\n" );
} );

If you run an SEO plugin that already manages Open Graph tags, feed your card into its filter instead of printing a duplicate. Most expose a hook for the image, and letting the plugin own the markup avoids two competing og:image tags.

Step 4: Backfill the catalogue

New and edited products are covered by the hooks. To generate cards for everything already in the store, run a one-off loop over published products.

$ids = wc_get_products( [ 'status' => 'publish', 'limit' => -1, 'return' => 'ids' ] );

foreach ( $ids as $id ) {
    ai_generate_product_card( $id );
}

For a large catalogue, batch this through Action Scheduler rather than a single request, so a few thousand renders do not time out. WooCommerce already ships Action Scheduler, so you can enqueue one job per product and let it work through the queue.

That is a complete, self-maintaining setup. Every product gets a branded card with its photo, name and current price, generated on save and served as the share image. Your product links stop looking like bare catalogue photos and start looking like ads you meant to make.