Automatic responsive videos in WordPress with oEmbed, FitVids and a little PHP magic

Automaitc responsive videos in WordPress with oEmbed, FitVids and PHP
UPDATE: I’ve added a 4th step to the list to remove automatic inline styles from being inserted when embedding videos.
If you’re using a responsive theme on your WordPress site (or you’ve built a responsive theme) and you’ve added YouTube, Vimeo or other videos using oEmbed you will undoubtedly have noticed those videos do not resize with the rest of the frame. And that’s a royal pain. Fortunately there are solutions out there that can fix this, but they are a little tricky to implement. In this tutorial I’ll share with you a method for making the process automatic so you don’t have to worry about it.

The Problem

Responsive themes use percentage values, media queries and other coding magic to make the content resize to fit the size of the window. But when you embed videos from YouTube etc using the built in oEmbed function in WordPress (i.e. just paste in the URL to the video and it appears automatically) that video is inserted with a fixed width and height. As a result when the rest of the page resizes to fit the window, the video stays the same size causing all sorts of problems. This is sub optimal.

FitVids to the rescue… almost

Realizing this is a frustrating problem Chris Coyier and Paravel created a clever little jQuery plugin called FitVids that when installed automatically resizes videos along with the rest of the content. FitVids attaches to specified containers and forces the video iframes within these containers to resize along with it. Very clever and it works exactly as expected. However, to make this work you have to wrap the video in a container with a specified class. So if you want to use the oEmbed method you have to go to HTML view, create a div with a class and then put the URL inside it. Which kind of takes away the whole point of using oEmbed which is simplicity.

The Solution

What is needed is a function that automatically wraps all oEmbed videos in a div with the correct class that applies FitVids so all the user has to do is paste in the link to the video and then WordPress does the rest. And that’s just what we’re going to do:

Step 1: Enqueue FitVids

Go download FitVids.js from GitHub and add the jquery.fitvids.js file to your theme. I place all my JavaScript files in a sub-folder called ‘js’ for simplicity. Then we need to enqueue the script and associate it with jQuery. This is done in functions.php:

This function loads the jquery.fitvids.js file along with the packaged version of jQuery that comes with WordPress whenever a page is loaded.

Step 2: Create a function to target the videos

To make FitVids work you need to add a JavaScript function targeting a specific class. The function looks like this:


All that happens here is the action is loaded in the footer (so it doesn’t slow down the population of the page itself and allows the videos in the iframes to load properly). It then appends the function to the .video class so that any video inside a div with the class video will be scaled to size.
This function is combined with the previous function so they get called at the same time. The resulting function looks like this:


Step 3: Automatically wrap oEmbed videos in a div with a class

The last step is to change the oEmbed output so that it automatically wraps the video iframe in a div with the class .video. This is done using a filter:

'; return $return; } add_filter('oembed_dataparse', 'your_theme_embed_filter', 90, 3 ); ?>

This function grabs the output of the oembed_dataparse function (the one that creates the final code when you paste in a video URL) and wraps it in the correct div.

Step 4: Set Maximum Embed Size to 0

To get everything to work properly you have to go to Settings -> Media and set both width and height under Maximum Embed Size to 0. If you have a value in either of these fields, WordPress will include inline style code to constrain the size of the video and as a result the automatic resizing will not work.

That is all! When you add new videos to posts and pages using the oEmbed function, they are not automatically wrapped in the correct div and class and FitVids is applied. And voila: Your videos are responsive.

Caveat: These functions are not recursive!

The only catch with this process is that it is not recursive. By that I mean it doesn’t automatically work on videos that have already been embedded on your site. That is because the oembed_dataparse() function is called the when the post is published or updated. As a result, the function has already been run on old content and to apply the new div and class you have to re-run it. Fortunately that just means going in and clicking the Update button for each of the posts that have oEmbed videos in them, but if you have hundreds of videos you may want to consider doing some sort of database search/replace action.
To avoid the recursive problem I suggest you add this function to your theme at the very beginning and be done with it. That way as you populate your site the all your videos will be responsive.

Comments? Questions? Problems?

Got something to say? Leave a comment below.

About Morten Rand-Hendriksen

Morten Rand-Hendriksen is a staff author at specializing in WordPress and web design and development and an instructor at Emily Carr University of Art and Design. He is a popular speaker and educator on all things design, web standards and open source. As the owner and Web Head at Pink & Yellow Media, a boutique style digital media company in Burnaby, BC, Canada, he has created WordPress-based web solutions for multi-national companies, political parties, banks, and small businesses and bloggers alike. He also contributes to the local WordPress community by organizing Meetups and WordCamps.


  1. Thanks for this detailed work around Morten.

    Is there a way that we can use the other functions when embedding youtube videos like – autoplay, no related videos and stripping the controls?

  2. After the 3.4 wordpress update and oembed tweets I face a similar problem when embedding tweets in my responsive theme (Twenty Twelve preview) They don’t resize with the responsive theme but rather just stay the size you can specify in wordpress settings. Is there any workaround for that?

  3. For those of us who don’t really do a lot of coding, would there be a simple way to create a short code with the post snippets plug-in in order to use post-vid div wrap each time you put an URL in the H TML?

  4. I came across this while trying to find an error within FitVids, so I just wanted to post the solution in case anyone else finds this post the same way I did… Internet Explorer 9 can sometimes cause problems when using FitVids and YouTube. This is because IE 9 does not support wrapping iframes using Javascript. The solution is to replace this code:

    $this.wrap(”).parent(‘.fluid-width-video-wrapper’).css(‘padding-top’, (aspectRatio * 100)+”%”);

    With this code:

    $this.prev(‘.fluid-width-video-wrapper’).css({ ‘padding-top’: (aspectRatio * 100)+”%” });

  5. Hi,

    I just finished the responsive theme tut. Love your work! I know Xmas is a few months away, but there are some areas that your wizardry could really help get my site running with the Anaximander theme before Santa arrives:

    1. What would the easiest steps be to have responsive self-hosted videos, rather than the Vimeo and YouTube options?

    2. Is the “fitvidsjs” plugin below an option or would some magic code make everything work?

    3. If the fitvids plugin is the answer what should I enter for the Add jQuery 1.7.2 from Google CDN (ticked or unticked) CSS selector?

    Btw, congrats on the “Staff Author” title. I have been through three of your tut’s and Lynda’s concept itself is amazing.

    Looking forward to hearing from you. As they say in Deutschland, “Bleib Kool” (stay cool),

    Big B

    1. Hi Big B. If you want to host your videos on your own server (not something I would recommend) then you can use the VideoJS plugin to embed HTML5 videos. This requires that you have a minimum of 3 different video files (OGG, WebM and MP4) and you should probably have a Flash fallback as well. With this option you just have to wrap it all in a standard

  6. Wow, that was fast! A reply in 10 mins!!

    I know that the self-hosted option has its drawbacks…my content is mostly pix and vids of my kids for family and friends. More “control” over the media I have, and am willing to share, is the caveat to hungry-space-eating videos and pix. I guess I will have to delve into Vimeo or YouTube and look at their options again. This new theme that you made is really great: a fresh start.

    I have spent many, many hours with your courses over the last weeks. There were a couple of things that threw me when my brain wasn’t functioning at 100%. Probably have been covered already, but just as a heads up:

    in “WordPress Essential Training”:

    Ch3 placing WordPress in a Directory:

    I couldn’t see the .htaccess file even after making files visible and invisible in Filezilla and in Forklift. I ended up installing a security plugin that created it.

    Ch8 Extending…with plugins

    I have only used one hosting company since I have been here in Germany. There is a “rights” section buried deep in the admin panel about permissions for using the FTP uploader and permission for using the WP to upload plugins etc. Caused me a lot of frustration before I understood what was happening when it asked for “ftp credentials”. How the “recursive” function works and what it means. Could be worth mentioning for other newbies like me if plugins aren’t installing via the FTP client or the WP installation.

    Responsive Theme Tut

    I am still trying to problem solve around the Flexislider. I copied the exercise files over directly into my FTP client, but the picture slider doesn’t appear. Any tips? I haven’t installed any feature images, does that make a difference?

    Wow, this has turned into a monster “comment”. Thanks so much for responding to my query earlier. Sounds like you have a lot on your plate with new courses, your biz and enjoying life in BC.


    Big B

    1. @Big B: Let me try to answer your questions one by one:

      - Different web hosts have different settings when it comes to the .htaccess file. Some provide it by default, some ask you to create it. If it’s not there and your site works, then don’t worry about it. WordPress will likely write a .htaccess file for you automatically once it’s needed.
      - FTP access also differs from host to host. Sounds like you sorted that out on your own.
      - Flexslider is in nearly constant development and will change from time to time. For absolute up-to-date documentation on how to use it you should check out the website at

  7. Very useful tut Morten, it helped me map out a plan for my own work – big THANKS!

    One little nugget I picked up along the way is that you can actually conditionally load resources like fitvid, not just by is_page() filters and the like, but even more precisely on posts and pages that actually have videos on them – no matter where the fall.

    To do this, we can use a short code to signal the dynamic loading (as of WordPress 3.3)

    Note: I’ve not extensively tested the code below in all situations, but it does seem to be pretty solid in my environment. Also this shortcode doesn’t help you embed the video like some short codes can, meerly acts as a flag, telling wordpress to only include the js on pages that need it – thus making your site a wee bit faster.

    First the short code: (lightly modified version of the above)
    function your_fitvids_shortcode($atts) {
    // add FitVids to site
    wp_register_script( 'fitvids', plugins_url('fitvid/jquery.fitvids.js', __FILE__), array('jquery'), '1.0', true);
    wp_enqueue_script( 'fitvids');
    // slecetor script
    if (!function_exists('add_fitthem')){ // we only need it to fire once!
    function add_fitthem() {
    ob_start(); ?>

    jQuery(document).ready(function() {
    jQuery(".oembed").fitVids({customSelector: "iframe[src^='']"});

    <? echo ob_get_clean();
    add_action('wp_footer', 'add_fitthem');
    add_shortcode('fitvid', 'your_fitvids_shortcode');

    the only thing left now is do, add the shortcode into the example in step 3.

    $return = '[fitvid]'.$output.'';

    Its helped me a lot, so did

  8. Hi Morten,

    Thanks for the great tutorial on responsive designs. I was just wondering about media queries – what do we do for ie7 and ie8 because they don’t seem to accept media queries?



    1. If you look in the files for Anaximander you’ll see a JavaScript file called html5.js. This file adds a shim to the site so that if older browsers are used, the media queries still work. The shim is included with conditional statements at the top of header.php.

  9. Thanks very for this. Brilliant tutorial

    I’m not much of a developer and have a few other functions in my functions.php

    I’ve created a twenty twelve child theme and can’t get this function to work even though I’ve put it at the top of my file

    jQuery(document).ready(function() {

    add_action('init', 'your_theme_fitvids');

    // Automatically add FitVids to oembed YouTube videos
    function your_theme_embed_filter( $output, $data, $url ) {

    $return = ''.$output.'';
    return $return;

    add_filter('oembed_dataparse', 'your_theme_embed_filter', 90, 3 );

    // Load up theme options page and related code.
    require( get_stylesheet_directory() . '/inc/theme-options.php' );

    // Change header width
    function tto_custom_header_setup() {
    $header_args = array(
    'width' => 1000,
    'height' => 250
    add_theme_support( 'custom-header', $header_args );
    add_action( 'after_setup_theme', 'tto_custom_header_setup' );

    // de-queue navigation js
    function tto_dequeue_navigation() {
    wp_dequeue_script( 'twentytwelve-navigation' );
    // load the new navigation js
    function tto_custom_scripts()

    // Register the new navigation script
    wp_register_script( 'lowernav-script', get_stylesheet_directory_uri() . '/js/navigation.js', array(), '1.0', true );

    // Enqueue the new navigation script
    wp_enqueue_script( 'lowernav-script' );
    add_action( 'wp_enqueue_scripts', 'tto_custom_scripts' );

    // Add the new menu
    register_nav_menus( array(
    'primary' => __( 'Top Menu (Above Header)', 'tto' ),
    'secondary' => __( 'Lower Menu (Below Header))', 'tto'),
    ) );

    // Add a Theme Options link to Admin Bar
    function theme_options_link()
    global $wp_admin_bar, $wpdb;
    if (!is_super_admin() || !is_admin_bar_showing())
    $wp_admin_bar->add_menu(array('parent' => 'appearance', 'title' => __('Theme Options', 'Theme Options'), 'href' => home_url() . '/wp-admin/themes.php?page=theme_options'));
    add_action('admin_bar_menu', 'theme_options_link', 1000);

    // Change default thumbnail size
    function tto_twentytwelve_setup() {
    set_post_thumbnail_size( 651, 9999 ); // (default featured images)Unlimited height, soft crop
    add_image_size( 'post-excerpt-thumbnail', 120, 120, true ); // Post excerpt Thumbnails
    add_action( 'after_setup_theme', 'tto_twentytwelve_setup', 11 );

    // Override content width (for photo and video embeds)
    $content_width = 651;

    function tto_content_width() {
    if ( is_page_template( 'page-templates/full-width.php' ) || is_attachment() || ! is_active_sidebar( 'sidebar-1' ) ) {
    global $content_width;
    $content_width = 1000;
    add_action( 'template_redirect', 'tto_content_width', 11 );

    // Body class for custom page template and 3 column layouts
    add_filter( 'body_class', 'tto_custom_body_class');
    function tto_custom_body_class( $classes ) {
    if( !is_page_template() )
    $classes[] = 'custom-layout';
    return $classes;

    // Set default background color for color schemes
    function child_default_background_color() {
    $theme_options = get_option( 'tto_theme_options');

    if ( 'dark' == $theme_options['color_scheme'] )
    $default_background_color = '181818';
    $default_background_color = 'E6E6E6';

    add_theme_support( 'custom-background', array(
    // set default background color in child theme
    'default-color' => $default_background_color
    ) );

    // Blog posts excerpt
    function get_the_post_excerpt(){
    $excerpt = get_the_content();
    $excerpt = preg_replace(" (\[.*?\])",'',$excerpt);
    $excerpt = strip_shortcodes($excerpt);
    $excerpt = strip_tags($excerpt);
    $excerpt = substr($excerpt, 0, 220); // change this to whatever you like
    $excerpt = substr($excerpt, 0, strripos($excerpt, " "));
    $excerpt = trim(preg_replace( '/\s+/', ' ', $excerpt));
    $excerpt = ''.$excerpt.' ID) . '">Continue Reading »';
    return $excerpt;

    //add a widget area in the header
    if ( function_exists ('register_sidebar') )
    register_sidebar( array(
    'name' => __( 'Header Widgets Area', 'twentytwelve' ),
    'id' => 'sidebar-header',
    'description' => __( 'A new header widgets area for my child them' , 'twentytwelve' ),
    'before_widget' => '',
    'after_widget' => '',
    'before_title' => '',
    'after_title' => '',
    ) );
    add_action( 'widgets_init', 'twentytwelve_widgets_init' );

    // Register footer widgets
    register_sidebar( array(
    'name' => __( 'Footer Widget One', 'tto' ),
    'id' => 'sidebar-4',
    'description' => __( 'Found at the bottom of every page (except 404s, optional homepage and full width) as the footer. Left Side.', 'tto' ),
    'before_widget' => '',
    'after_widget' => '',
    'before_title' => '',
    'after_title' => '',
    ) );

    register_sidebar( array(
    'name' => __( 'Footer Widget Two', 'tto' ),
    'id' => 'sidebar-5',
    'description' => __( 'Found at the bottom of every page (except 404s, optional homepage and full width) as the footer. Center.', 'tto' ),
    'before_widget' => '',
    'after_widget' => "",
    'before_title' => '',
    'after_title' => '',
    ) );

    register_sidebar( array(
    'name' => __( 'Footer Widget Three', 'tto' ),
    'id' => 'sidebar-6',
    'description' => __( 'Found at the bottom of every page (except 404s, optional homepage and full width) as the footer. Right Side.', 'tto' ),
    'before_widget' => '',
    'after_widget' => "",
    'before_title' => '',
    'after_title' => '',
    ) );

    // Use WP-PageNavi when it's active
    function twentytwelve_content_nav( $nav_id ) {
    global $wp_query;
    if ( $wp_query->max_num_pages > 1 ) : ?>

    <nav id="">

    <?php next_posts_link( __( '← Older posts', 'tto' ) ); ?>
    <?php previous_posts_link( __( 'Newer posts ←', 'tto' ) ); ?>

    <?php endif;


  10. I just want to thank you for everything that I learned from you, I’m a premium member on and I looked at all your MASTERCLAS play list. You’re the man :)
    I please tell me how to remove

    You may use these HTML tags and attributes:

    under comment

  11. I’m amazed, I must say. Seldom do I encounter a blog that’s equally educative and amusing, and without a doubt, you’ve hit the nail on the head. The issue is an issue that too few folks are speaking intelligently about. I’m very happy I found this during my hunt for something concerning this.

  12. I was so glad to find this solution, but even following the directions meticulously and trying several separate times, it’s not working for me.

    I did note that the videos need to be added *after* adding the JS and PHP.

    Vimeo videos don’t “respond” at all.

    Any ideas what I might be doing wrong? Anything I can check?

    Thanks in advance -

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>