Add Twitter Card and social meta tags to WordPress without a plugin

When someone shares a link on Twitter, LinkedIn, or Slack, those platforms fetch the page and look for specific meta tags to decide how to render the preview card — what title, description, and image to display. Without these tags, the preview is either a plain URL with no visual context or a generic, unappealing box that gets far fewer clicks. Twitter uses its own twitter:card, twitter:title, twitter:description, and twitter:image tags. LinkedIn and Slack both fall back to the Open Graph tags (og:title, og:description, og:image) that were covered in the Open Graph guide. So adding Twitter Card tags alongside Open Graph tags covers virtually every major sharing platform in one pass. The twitter:card value controls the card layout: summary shows a small square thumbnail; summary_large_image shows a full-width banner image. For blog posts, summary_large_image with a 1200×630px featured image gets the best engagement. The image must be publicly accessible and at least 144×144px. Twitter also requires the twitter:site tag with your Twitter handle for some card features. You can validate your implementation with Twitter’s Card Validator at cards-dev.twitter.com/validator. The snippet below combines Twitter Card and Open Graph tags into a single wp_head callback and is compatible with the JSON-LD schema guide for a complete metadata setup.

Problem: When your WordPress posts are shared on Twitter or LinkedIn, they show a plain URL with no preview image or description, resulting in low click-through rates.

Solution: Add the following code to your functions.php file:

add_action( 'wp_head', 'helloadmin_social_meta_tags', 5 );
function helloadmin_social_meta_tags() {
    // Determine page-specific values
    if ( is_singular() ) {
        $title       = get_the_title();
        $description = wp_strip_all_tags( get_the_excerpt() );
        $url         = get_permalink();
        $image       = '';
        if ( has_post_thumbnail() ) {
            $img   = wp_get_attachment_image_src( get_post_thumbnail_id(), 'large' );
            $image = $img ? $img[0] : '';
        }
    } else {
        $title       = get_bloginfo( 'name' );
        $description = get_bloginfo( 'description' );
        $url         = home_url( '/' );
        $image       = '';
    }

    // Fallback image (set to your default sharing image)
    if ( ! $image ) {
        $image = get_template_directory_uri() . '/images/default-share.jpg';
    }

    $twitter_handle = '@helloadmin'; // change to your Twitter username

    // Open Graph tags (Facebook, LinkedIn, Slack, WhatsApp)
    $og = [
        'og:type'        => is_singular() ? 'article' : 'website',
        'og:title'       => $title,
        'og:description' => $description,
        'og:url'         => $url,
        'og:image'       => $image,
        'og:site_name'   => get_bloginfo( 'name' ),
    ];
    foreach ( $og as $property => $content ) {
        if ( $content ) {
            printf(
                '<meta property="%s" content="%s">' . "
",
                esc_attr( $property ),
                esc_attr( $content )
            );
        }
    }

    // Twitter Card tags
    $twitter = [
        'twitter:card'        => 'summary_large_image',
        'twitter:site'        => $twitter_handle,
        'twitter:title'       => $title,
        'twitter:description' => $description,
        'twitter:image'       => $image,
    ];
    foreach ( $twitter as $name => $content ) {
        if ( $content ) {
            printf(
                '<meta name="%s" content="%s">' . "
",
                esc_attr( $name ),
                esc_attr( $content )
            );
        }
    }
}

NOTE: The recommended image size for summary_large_image Twitter Cards and Open Graph is 1200 × 630 pixels with a maximum file size of 5MB. Images smaller than 300×157px will not be shown as large cards. If you have an SEO plugin (Yoast, RankMath, AIOSEO) already installed, it outputs its own OG/Twitter tags — adding this snippet will create duplicate meta tags, which causes validation warnings. Either remove this snippet or add a check at the top: if ( defined( ‘WPSEO_VERSION’ ) ) { return; }.