Customizing WordPress oEmbed Content

    James DiGioia
    James DiGioia

    WordPress makes it very easy to embed content. For example, you can grab a YouTube URL, drop it into the post editor, and automatically see a video embedded in your post. This is all powered by oEmbed, an open standard for how a page should provide embed code data.

    WordPress relies on this standard in order to fetch and display a provided URL. While WordPress has long been able to consume oEmbed providers like YouTube, with the inclusion of the WP-API in WordPress 4.4, WordPress can now function as an oEmbed provider. This means other sites can embed your WordPress content on their sites as easily as you can currently embed videos as mentioned in the example above.

    The new oEmbed provider functionality leverages the WP-API to return JSON or XML structured embed data to oEmbed consumers, giving them the information they need to embed the page on their own. Like everything in WordPress, the oEmbed provider functionality is easily modifiable with actions and filters. Let’s take a look at how we can use these hooks to embed a custom post type.

    oEmbed WordPress

    What We’ll Be Building

    We’re going to be building a simple plugin, ‘Status Update’, which registers a custom post type (CPT), status-update. This CPT simply removes the title input in the post editor, making it function like a Facebook or Twitter status update. We’ll customize the oEmbed output of this post to show the user’s name and avatar. You can see the final product in this GitHub repo.

    Setting up the Plugin

    Go to your plugins folder in wp-content and create a folder named sp-status-update. In that directory, create a file named sp-status-update.php. This is where we’ll be creating the plugin’s main class. Add the plugin header to the top of the file:

     * Plugin Name: Status Update
     * Plugin URI:
     * Description: Post your own Facebook-like status updates
     * Version:     1.0.0
     * Author:      James DiGioia for SitePoint
     * Author URI:
     * Text Domain: wp-status-update
     * Languages:   /languages

    We’ll then set up the main plugin singleton on which we’ll build our plugin:

    class SP_Status_Update {
         * Plugin instance.
         * @var static
        protected static $instance;
         * Retrieve the plugin instance.
         * @return static
        public static instance() {
            if (null === static::$instance) {
                static::$instance = new static;
            return static::$instance;
         * Plugin constructor.
        protected function __construct() {
            // Add actions/filters here

    Finally, we need to start the plugin at the bottom of the file:


    We’ll be adding our hooks and filters in the constructor, as noted in the comment.

    Register a Custom Post Type

    Using the excellent GenerateWP, we can easily customize and register our custom post type. Register the method with WordPress:

        add_action( 'init', array( $this, 'register_post_type' ), 0 );
    and add the registration method to your class:
         * Register the Status Update custom post type.
    public function register_post_type() {
        $labels = array(
            'name'                  => _x( 'Status Updates', 'Post Type General Name', 'sp-status-update' ),
            'singular_name'         => _x( 'Status Update', 'Post Type Singular Name', 'sp-status-update' ),
            'menu_name'             => __( 'Status Update', 'sp-status-update' ),
            'name_admin_bar'        => __( 'Status Update', 'sp-status-update' ),
            'archives'              => __( 'Satus Update Archives', 'sp-status-update' ),
            'parent_item_colon'     => __( 'Parent Update:', 'sp-status-update' ),
            'all_items'             => __( 'All Updates', 'sp-status-update' ),
            'add_new_item'          => __( 'Add New Status Update', 'sp-status-update' ),
            'add_new'               => __( 'Add New', 'sp-status-update' ),
            'new_item'              => __( 'New Status Update', 'sp-status-update' ),
            'edit_item'             => __( 'Edit Status Update', 'sp-status-update' ),
            'update_item'           => __( 'Update Status Update', 'sp-status-update' ),
            'view_item'             => __( 'View Status Update', 'sp-status-update' ),
            'search_items'          => __( 'Search Status Updates', 'sp-status-update' ),
            'not_found'             => __( 'Not found', 'sp-status-update' ),
            'not_found_in_trash'    => __( 'Not found in Trash', 'sp-status-update' ),
            'featured_image'        => __( 'Featured Image', 'sp-status-update' ),
            'set_featured_image'    => __( 'Set featured image', 'sp-status-update' ),
            'remove_featured_image' => __( 'Remove featured image', 'sp-status-update' ),
            'use_featured_image'    => __( 'Use as featured image', 'sp-status-update' ),
            'insert_into_item'      => __( 'Insert into Status Update', 'sp-status-update' ),
            'uploaded_to_this_item' => __( 'Uploaded to this Status Update', 'sp-status-update' ),
            'items_list'            => __( 'Status Updates list', 'sp-status-update' ),
            'items_list_navigation' => __( 'Status Updates list navigation', 'sp-status-update' ),
            'filter_items_list'     => __( 'Filter Status Updates list', 'sp-status-update' ),
        $args = array(
            'label'                 => __( 'Status Update', 'sp-status-update' ),
            'description'           => __( 'Simple Status Update', 'sp-status-update' ),
            'labels'                => $labels,
            'supports'              => array( 'editor', 'author', ),
            'hierarchical'          => false,
            'public'                => true,
            'show_ui'               => true,
            'show_in_menu'          => true,
            'menu_position'         => 5,
            'show_in_admin_bar'     => true,
            'show_in_nav_menus'     => true,
            'can_export'            => true,
            'has_archive'           => true,
            'exclude_from_search'   => false,
            'publicly_queryable'    => true,
            'capability_type'       => 'page',
        register_post_type( 'sp_status_update', $args );

    Now we’re ready to start customizing our oEmbed output!

    Before We Start: How oEmbed Works

    When you drop a link into the TinyMCE editor, how does it know how to embed that URL live in your editor?

    The first step is discovery. oEmbed asks providers “to make their oEmbed support discoverable by adding elements to the head of their existing (X)HTML documents.”. Those elements direct oEmbed consumers to the API endpoints, which then provide a structured representation of the embedded post. oEmbed supports both JSON and XML formats.

    WordPress includes elements and support for both formats. The function [wp_oembed_add_discovery_links], which is registered on wp_head, is responsible for including the proper <head> tags and can be customized with the oembed_discovery_links filter.

    From there, the editor grabs the structured version from the API endpoint. The JSON response looks like this:

      "version": "1.0",
      "provider_name": "Website Name",
      "provider_url": "",
      "author_name": "admin",
      "author_url": "",
      "title": "",
      "type": "rich",
      "width": 600,
      "height": 338,
      "html": "long string of html"

    The XML response is structured the same way, and WordPress provides support for both. It leverages the WP-API to provide both the JSON and XML formatted responses, although the XML response required special handling.

    The editor uses this response, sanitizing the html key and injecting it into the page. In WordPress, the oEmbed HTML contains a blockquote with the post title , a <script> tag for safely and correctly handling iframe messages, and a sandboxed iframe. This HTML can be fetched with [get_post_embed_html] and filtered with embed_html.

    The iframe URL is where all the magic happens. By default, it imports the built-in embed template, but you can override it completely and use your own embed template. Use the embed_template filter and return your own template file to swap it out completely:

    add_filter( 'embed_template', 'my_embed_template' );
    function my_embed_template( $template ) {
        if ( 'custom_post_type' === get_post_type() ) {
          return '/path/to/custom-embed-template.php';
        return $template;

    For the most part, you’re not going to need to customize any of the above filters, but it’s good to familiarize yourself with the internals of the oEmbed provider in order to leverage it more effectively. If you need that power, it’s there. We’re going to modify the built-in template with the hooks and filters it makes available to us.

    Customizing the oEmbed Title

    Since the plugin is embedding a status update, the first thing we should do is remove the title from the output of the oEmbed. To do that, we need to hook into the the_title filter:

    add_filter( 'the_title', array( $this, 'remove_embed_title' ), 10, 2 );

    In the remove_embed_title, we’ll meet the first helper function provided: is_embed. This function returns true when we’re in the oEmbed context, which we can use to customize the oEmbed output.

    Here’s how we remove the title:

     * Remove the title from the Status Update oembed.
     * @param string $title Post title.
     * @param int    $id Post ID.
     * @return string
    public function remove_embed_title( $title, $id ) {
        $post = get_post( $id );
        if ( is_embed() && 'sp_status_update' === $post->post_type ) {
          return '';
        return $title;

    This ensures the title is only removed in the oEmbed. Now, if we were building this plugin out in a more complete way, we may not need to include the is_oembed check, as you’ll probably just remove it everywhere, but this demonstrates how we can target and customize the oEmbed title output.

    Customizing the oEmbed Excerpt

    Because the Status Updates are supposed to be small pieces of writing, we don’t want to display an excerpt but the entirety of the post. We can modify what gets excerpted by filtering the the_excerpt_embed hook:

    add_filter( 'the_excerpt_embed', array( $this, 'get_excerpt_embed' ) );

    We then check if the current post type is our custom post type, and if it is, we return the full content output:

     * Returns the custom excerpt for the custom post type.
     * @param  string $output Default embed output.
     * @return string         Customize embed output.
    public function get_excerpt_embed( $output ) {
        if ( 'sp_status_update' === get_post_type() ) {
          return get_the_content();
        return $output;

    This ensures the excerpt is always the full post content for the Status Update. In the iframe, that excerpt is output in a div with class wp-embed-excerpt, to which you could apply custom styles. If you wanted to do something more complex, like a calendar event, you could output your custom content in that div, or you could append your own content with the embed_content action.

    Adding Extra Content to oEmbed

    The oembed_content action fires right after the excerpt is output, providing a location between the excerpt and the footer to output your own custom HTML. For the Status Update plugin, we’re going to use it to output the author’s name and avatar. First, we add the action:

    add_action( 'embed_content', array( $this, 'embed_author' ) );

    We can use this action hook to echo out our custom HTML:

     * Add the author div to the embed iframe.
    public function embed_author() {
        if ( 'sp_status_update' !== get_post_type() ) {
        $output = '<div class="wp-embed-author">';
        $output .= '&mdash; ';
        $output .= get_the_author();
        $output .= get_avatar( get_the_author_meta( 'ID' ), 20 );
        $output .= '</div>';
        echo $output;

    We have a simple emdash, the authors name, and the author’s avatar. Again, if you need more extensive customization, this action gives an opportunity to output any additional custom HTML.

    Adding Custom Styles and Scripts to oEmbed

    Like a standard template page, the oEmbed template has a header and footer. The hook embed_head gives you a location to enqueue your own styles in the head of the template. Like we have been, attach your method to the hook:

    add_action( 'embed_head', array( $this, 'embed_styles' ) );

    We can use this hook to enqueue our own styles with the standard wp_enqueue_style function, or if we don’t have that many styles to include, we can echo it out directly in the head:

     * Embed the plugin's custom styles
    public function embed_styles() {
        echo <<<CSS
      .wp-embed-excerpt, .wp-embed-author {
        font-size: 24px;
        line-height: 24px;
        margin-bottom: 5px;
      .wp-embed-author {
        float: right;

    On the JavaScript side, we can do the same using the embed_footer action, including using the wp_enqueue_script function to enqueue new JavaScript files, or output any custom JavaScript directly in-line.


    These are the primary hooks you’ll need to interact with in order transform the output to your preferences. All of the code in this tutorial can be found in the GitHub repo.

    If you want to see these customizations in action, keep an eye on the next version of WP-Gistpen, which will leverage the WordPress oEmbed provider in order to embed syntax highlighted code snippets.

    How are you planning on using the oEmbed provider feature in WordPress? Let me know in the comments!

    Frequently Asked Questions (FAQs) about Customizing WordPress oEmbed Content

    What is oEmbed and how does it work in WordPress?

    oEmbed is a protocol that allows an embedded representation of a URL on third party sites. The protocol allows you to use the URL from a third-party website and automatically transform it into an embedded piece, such as a video or image. In WordPress, oEmbed works by recognizing URLs from large providers like YouTube, Twitter, or Instagram. When you paste these URLs into your post editor, WordPress automatically turns them into embeds.

    How can I customize the appearance of my oEmbeds in WordPress?

    Customizing the appearance of your oEmbeds in WordPress can be done through CSS. You can target the embed class in your theme’s CSS and apply the desired styles. For instance, you can change the border, background color, or margins of your embeds. Remember to clear your cache after making changes to see the effects.

    Can I disable oEmbed in WordPress?

    Yes, you can disable oEmbed in WordPress. This can be done by adding a simple line of code to your theme’s functions.php file. Disabling oEmbed might be useful if you want to improve your site’s performance or if you prefer to manually control how your embeds look.

    What are some common issues with oEmbed in WordPress?

    Some common issues with oEmbed in WordPress include the embed not displaying correctly, the embed causing layout issues, or the embed not working at all. These issues can usually be resolved by checking the URL, ensuring your theme supports oEmbed, or troubleshooting potential plugin conflicts.

    How can I use oEmbed with custom post types in WordPress?

    To use oEmbed with custom post types in WordPress, you need to ensure that your custom post type supports the ‘editor’ feature. If it does, you should be able to use oEmbed just like you would in a standard post or page.

    Can I use oEmbed with non-supported websites in WordPress?

    While WordPress supports oEmbed for a large number of providers, you might come across a website that isn’t supported. In this case, you can use a plugin like oEmbed Plus, which extends the list of supported providers.

    How can I control the size of my oEmbeds in WordPress?

    The size of your oEmbeds in WordPress is determined by your theme’s content width. However, you can control the size by adding a filter to your theme’s functions.php file or by using a plugin that allows you to specify the dimensions.

    Can I use oEmbed in WordPress widgets?

    Yes, you can use oEmbed in WordPress widgets. Simply paste the URL into a text widget and WordPress will automatically turn it into an embed.

    How can I add oEmbed support to my WordPress theme?

    To add oEmbed support to your WordPress theme, you need to add a few lines of code to your theme’s functions.php file. This code tells WordPress that your theme supports oEmbed and specifies the content width for your embeds.

    Can I use oEmbed in WordPress comments?

    Yes, you can use oEmbed in WordPress comments. However, this feature is not enabled by default. You can enable it by adding a filter to your theme’s functions.php file.