How to Create Responsive Tables in WordPress

Use WordPress? Do you have tables in your posts?

These can be tricky to edit as well as display properly on a mobile display.

I’ve already rounded up a bunch of possible solutions, but here I’m going to break it down further, and outline what I’ve been doing.

1. Adding and Editing Tables

If really want to understand HTML tables, read Chris Coyier’s comprehensive guide.

It is possible to add and edit a table in WordPress in the visual editor. To do this you need to activate the table button in the toolbar.

Do this by adding a plugin called MCE Table Buttons.

On the visual editor, make sure your click the rightmost icon (the toolbar toggle shows the enhanced toolbar).


Now the toolbar shows a second line – including a new TABLE button.


This button has a dropdown menu with a number of options for creating a table.

  1. Insert the table according to the number of rows and columns you need.
  2. Click in the first row. Then drill down the table button: Row / Table Row Properties. Set the Row Type to header.
  3. Select the first row. Using the table button: Cell / Table Cell Properties. Set the Cell Type to Header cell.

Don’t drag the table to resize it. This will add in fixed widths which will not go well on a responsive site.

This will give us a semantically correct table. The source looks like this:


Formatting the Table

All formatting should be done with CSS. Your table should not contain widths in the HTML or other formatting markup.

Most themes will have their default table styles. From here you can either edit the theme CSS. Or, add your extra table CSS into the Custom CSS box on your theme options (most commercial themes have this).

Beverage Short caffeine Tall caffeine Grande caffeine Venti caffeine
Brewed Coffee 180mg 260mg 330mg 415mg
Brewed Decaf Coffee 15mg 20mg 25mg 30mg
Caffè Americano 75mg 150mg 225mg 300mg
Caffè Latte 75mg 75mg 150mg 150mg

Some of the more common formatting you might want:

Striped Rows
tbody tr:nth-of-type(2n) {background-color: #f0f0f0;}

Nice Heading Row
th {background-color:#018DB1;font-weight:bold;color:#fff;}

Bolded First Column
tbody tr td:nth-of-type(1) {font-weight: bold;}

Formatting for Mobile Layouts

Set a media breakpoint in your CSS according to your design. In this example any layouts narrower than 600px will get the mobile optimized table.

You can resize your browser to see this in action. I’ve pasted a screen capture to show you.


@media screen and (max-width: 600px) {
table {width:100%;}
thead {display: none;}
tr:nth-of-type(2n) {background-color: inherit;}
tr td:first-child {background: #f0f0f0; font-weight:bold;font-size:1.3em;}
tbody td {display: block;  text-align:center;}
tbody td:before { 
    content: attr(data-th); 
    display: block;

Getting Column Headings to Appear in Each Cell

Props to Dudley Story. This code has been adapted from that example.

We need to go through each table and extract out heading text. Then concatenate this into each cell.

jQuery is not required. This code must be placed before the </body> tag, but make sure it is after the content of your page. Many themes also have this as an option. If you don’t have table headers (are not using thead) the code will still work.

(function () {
	var headertext = [];
	var headers = document.querySelectorAll("thead");
	var tablebody = document.querySelectorAll("tbody");
	for(var i = 0; i < headers.length; i++) { headertext[i]=[]; for (var j = 0, headrow; headrow = headers[i].rows[0].cells[j]; j++) { var current = headrow; headertext[i].push(current.textContent.replace(/\r?\n|\r/,"")); } } if (headers.length > 0) {
		for (var h = 0, tbody; tbody = tablebody[h]; h++) {
			for (var i = 0, row; row = tbody.rows[i]; i++) {
			  for (var j = 0, col; col = row.cells[j]; j++) {
			    col.setAttribute("data-th", headertext[h][j]);
} ());

That’s about it.

You don’t have to use large jquery plugins.

Please see also this example, dealing with multiple tables with and without header rows.

Hi, I'm James, and for the last decade I've made a living by making my own blogs and websites.
Updated: August 16, 2017


  1. Hi,
    I am new to WordPress and made my first breeding website with Dreamweaver. Now I have some problems with the layout of the pedigrees of my dogs in the mobile view. It works fantastic in the desktop and tablet view but not in the mobile view (I think because of the rowspans and no table head):

    Do you perhaps have an idea for me how I can optimize it?

    Thank you very much.

    Kind regards from Germany,

    • It’s looking pretty good to me!

  2. What code would you input into a table to make just that table unresponsive to your responsive coding? Everything works great with your responsive code but I find myself needing go back using one table so I can scroll around.

    • I guess the quickest laziest hack would be to have that table without the TBODY tag.

  3. This worked great for me! Thanks!

  4. This is by far the best method I’ve come across to implement on (WordPress) sites that we then hand over to clients for content management. No classes to add in the markup, no horizontal scrolling, no disclosing triangles. Simple, beautiful.

    • NB: Be aware that MCE Table Button is a handy plugin-extension for MCE Editor, but it doesn’t generate correct markup for the header:

      This is what it generates for a basic 2×2 table:

      Just sayin…

      • OK, html stripped from comments!

        • Yep, the MCE table is not ideal for correct markup – but it’s a start.

  5. Hey James. =)
    I just discovered your trick and tried to make it work for my table, but the result wasn’t quite as I wanted. I think it’s because of my table’s structure, maybe you can help me.
    There is the table I’d like to show beautifully on narrow screens:
    The header is the grey part. For narrow screens it would be perfect if the two colums weren’t side by side but one below the other.
    DO you know some more magic tricks to resolve this?

    • Your table markup is not quite right. You are missing the thead tag which should wrap around the header row. See the HTML example above.

  6. Thank you James – works beautifully. Love the mobile version.
    Just one question: what do I target to reduce the amount of space around the text? On all but screen width of 1920 I’m forced to use the mobile version media query. If I can reduce the amount of padding around the text it will make my table just that bit smaller.

    • You want to check padding for the td tag.

      td {padding:0}

      in your css will remove all padding from each cell.

      • Thanks James – had tried that already but this time added !important and now it works!

  7. I had trouble with multiple tables on a page if they didn’t all have theads or th cells: the headers would be collected from all tables and then applied in order to all table cells regardless of whether they came from that table or another table.

    I also looked at which is table-id-specific, but has the ID hard-coded into the script.

    I made which first adds a unique ID to each table on the page that doesn’t already have one, and then collects and applies the header data within each table – skipping tables that don’t have theads (or that have only 1 or 2 columns, because I don’t want to collapse small tables).

    • Awesome work. After using this code in another site I had, I noticed the thead issue. I had one table that didn’t have it and the code messed up. I’ll put a small fix in here, and also link to your jsfiddle.


  8. This is too confusing. I can’t even find the table in my dashboard.



  9. My table is all set up as per your instructions and it works beautifully when I resize my desktop browser, but it doesn’t work on my cellphone. Canyou help me?

    Thanks! Ben

  10. Hello James,

    I have recently wrote a customized plugin for my application to convert any table to be a mobile responsive. My application needs many other features.But I am so amazed , how you nailed this code and how simple it was. Apart from the customized features I have, The way you have used responsive features were simply super awesome. My plugin looks ridiculous now. I will modify your CSS, to simple add the features I have like collapsing , sorting etc.


  11. This will center everything for Tablepress:
    Enter code into Plugin Options in the Tablepress Custom CSS window

    .tablepress thead th,
    .tablepress tbody td {
    text-align: center;
    vertical-align: middle;
    .tablepress tbody td img {
    display: block;
    margin: auto;

    I originally wanted the tables to be on a desktop puter to be aligned left and on mobile to be centered but I think overall it’s better to make everything centered.

    I hope someone else finds this useful.

  12. This only works on1 table on the page. I changed the js to use a class instead of id, added more tables…it still works for the first table on my page, but none that follow.

    Any modifications to use on multiple tables within 1 page?

    • The code already loops thru all theads and tbody tags (there is no selection by ID – so not sure what you mean there). I have the code running on a site where there are multiple tables.

  13. Works great with the Monstroid theme tables but your mobile table is having an issue centering td text in the WordPress plugin TablePress it stays flush left. Do you have or can you make a fix for this?
    I am trying to figure it out but with no luck yet,

    • Tablepress must also have some CSS to text-align:left on all TD.

      In the above CSS you could try this:

      text-align:center !important;

  14. Dear James, When there is more than 2 table then header of below one is overlapping on another. Please have a look. What is wrong and what is solution. your help is appreciated.

    • Sandeep can you paste in an example URL to look at?

      • Hello James, Can you provide email ID to review code? Here is link

        Problem has 2 input table and 2 out put table. You can look that on minimizing, Height, Length, thickness if table is overridden by Width(Shortest Dimension(ft)), Length(Longest Dimension(ft)) and Concrete Topping(in) of second table.

  15. Beautiful!! Thanks so much. Been trying to figure out a solution to this for a while. Happy it’s plugin free too! 🙂

  16. I wish WordPress added a very good tables inbuild in the next release. It should be basic feature. Thanks for your code. I hope it works for me.

    • You’d have to completely customize the javascript to concatenate the first and second header rows together.

      • Ok, thanks, I’ll pass that on to my Js dev.

        Appreciated 🙂

          • Tabular data on mobile is difficult – by nature tables are for large displays. I think it’s fine what you’ve got. One thing I would like to work on is an ‘accordion’ style table where you can show/hide rows.

      • My dev says he has to use jquery to make the customization work. Does that sound right to you?

        • jQuery certainly makes things easier — but seems a shame to have to load the jQuery library just for one purpose.

          • Tell me about it!

  17. I use a lot of tables on my site and this is great, any chance of making the table an accordion so all you see is the head of the table until you click it and then the body displays underneath?

    • Good idea, although the biggest challenge is indicating to the user that they can tap to display more.

  18. Hi Kevin,
    Thanks for sharing this code. I’m trying to make mobile-friendly a simple 3 X 2 table, without headings. So I’m ok with each cell appearing in new row in mobile version. But it never works with this code.
    Please, let me know what should I modify here.

    Thank you

    • Kate, I’ve finally updated the code to fix a Javascript error that happened if you didn’t have thead in your table. If your having problems still, let me know your URL and I can take a look.

  19. Thanks for Your article … it work nice

    by the why your theme so beautiful

  20. headertext[h] is undefined

  21. The code doesn’t works for explorer edge

  22. Not sure why but it is stripping out my TH data? Css is correct and js is executing?

  23. Hi. Great script. I can only get it the mobile column headers to work on the first table on a page… Should it work for all tables on the page?

    • Yes, all tables on the page. Check that each table has both a thead and tbody.

  24. I have more than one table on my page and only want headers on one of the tables. Can you show me how to limit the script to a certain table ID or something like that to control it?

    • You can add an ID to your table:

      <table id=”customers”>

      In the CSS you’ll need to create an extra rule:

      #customers tbody td:before {
      color: #444;

      Because it’s an ID it will override the other color rule.

      • Hello James, I do not know JS. Do I need to add the table name to the script somewhere to limit headers only added to the named table:

        var headertext = [];
        var headers = document.querySelectorAll(“thead”);
        var tablebody = document.querySelectorAll(“tbody”);

        for (var i = 0; i < headers.length; i++) {
        for (var j = 0, headrow; headrow = headers[i].rows[0].cells[j]; j++) {
        var current = headrow;

        for (var h = 0, tbody; tbody = tablebody[h]; h++) {
        for (var i = 0, row; row = tbody.rows[i]; i++) {
        for (var j = 0, col; col = row.cells[j]; j++) {
        col.setAttribute("data-th", headertext[h][j]);

        • The code is designed to efficiently grab every table on the page.

          You could do something like this.

          var headers = document.querySelectorAll(“thead.domobile”);
          var tablebody = document.querySelectorAll(“tbody.domobile”);

          Then in your HTML add class to both the thead and tbody element of the table you want to render in mobile:

          <thead class=”domobile”>

          <tbody class=”domobile”>

          This is just off the top of my hide. Not sure if it works.

    • Change this CSS, and put in your decided color. This will only color the prepended heading text.

      tbody td:before {
      content: attr(data-th);
      display: block;
      color: blue;

      • I will try it. Thank you.

      • Works great! Thank you.

  25. Small correction:
    For the “Nice Heading Row” the code reads
    th {background-color:#018DB1;font-weight:bold;color:#fff;}

    It should say “thead” instead of “th”

    Thanks for sharing, it’s helped me style my tables way better!

    • Sorry, nevermind! I was using this on a table in WordPress which had a tag but didn’t have tags in it, so that’s why it wasn’t working!

      Just wondering though, is it not better to style the entire rather than the individual heading items ()?

      Thanks again!

  26. There is a conflict with the JS.
    common.js:884 Uncaught TypeError: Cannot read property ‘0’ of undefined(…)

    It shows the error at this line below;
    col.setAttribute(“data-th”, headertext[h][j]);

    Why would it be giving me this error?

    • It means you are missing a thead and/or th in your tables. I need to fix the code to better account for this. The tables need to have a heading line so the code can figure out what to put in each cell.

        • You are missing thead in your table markup. The first row should be inside a thead tag. See the example table markup above.

  27. Have you included the example Javascript in your site?

  28. Hey,
    thanks for this blog entry! I’ve been looking for something like this everywhere. It worked pretty well, however, I can’t seem to figure out how to make the second row in the mobile version. I guess I have to customize the provided css code but I can’t seem to get it right.
    Is it even possible to only hide an entire row in the mobile version?
    Thank you!

  29. For those of you who aren’t coders or css savvy, I found an easy to use WP plugin. “Responsive Scrolling Tables detects when tables are bigger then their containers and makes them scroll instead of flowing over the boundary of the container.”

    You can find it in WP plugin directory – Responsive Scrolling Tables

  30. Thank you, but thank you very much! You rock! I have found at least what I am looking for long time…
    Thank you for your time writing this article about responsive table.


  31. Oh and did I mention that the plugin is mobile responsive too? You can set just how much space the table will consume, no matter the size of the screen!

  32. Thanks for sharing this. Do you know if it is possible to stylize the data-th text using css?

    I would like that text to appear as simply bold and black, without altering the style of what’s already in the respective , which in my case is green. Thanks for any suggestions.

    • See the styles for tbody td:before {}

      Just add some more CSS inside there.

  33. Very useful. Thank you so much

  34. Thank u James….

  35. Wonderful article. Could you or someone tell me how do I get this type of table – “” under “QUICK NAVIGATION”,

    I need
    *background gray
    *background blue on quick navigation

    Any idea James?

    • Solved by myself. In Genesis theme there is a Script session on a page, but it inserts the code in the html , not after the content.

  36. That’s awesome to see this awesome post. I’m trying to make a responsible table in many ways but your way (with Javascript) bring me to heaven quickly. It saves my day!

  37. Hi
    I am new to WordPress and am trying to insert a table into a web page. I have installed the Easy Table Plugin which led me to believe that this would simply add an icon onto my existing toolbar, allowing me to insert a table? Is this not the case?

    Thank you.

    • I took a quick look at the easy table plugin – this uses shortcodes to create tables.

      Shortcodes are something in wordpress where you use square brackets in your post. The plugin then renders these shortcodes into HTML.

      Like [table] would result in WordPress creating all the table HTML.

      In my post here the only plugin I use is one (MCE Table Buttons) that adds an extra button to the formatting toolbar. Perhaps this is what you were intending to use.

  38. Just what I was looking for, many thanks. I am using Tablepress and it all works fine for one little detail, the td-text and theader are left aligned on mobile, like this:

    Any ideas?

    • You’ll want to contact the authors of tablepress. However it could be nothing more than checking the text-align styles for the table and td.

  39. Hey,

    awesome script, Thanks a lot!

    It only seems to have a bug when the table heading has two words. When scaled down, it drops the last letter of the first word. Example:

    full width / scaled down:

    any Idea what might be causing this?

    • Luca can you copy and paste in the code again. I’ve removed a regular expression that seems to cause some grief for some people. Should work fine now.

  40. Thx man for this! Everything is working as i wish 🙂

  41. I like the idea and the way it looks, but the default Android browser turns it into a mess when you rotate the screen. Each time you rotate it back, it seems to get worse.

    Not sure why. It works fine in Chrome on the same phone, but I’m guessing more people just use the default browser.

  42. Thank you!

  43. Hi

    Tnx alot.

  44. Hi James, belay that request please. I just checked on a Kindle Fire and it looks great. I first tried a phone and the screen is simply too small to cope with all the data. I doubt many, if any on my uses will use a phone to see the site. Thank you once again.

  45. Many thanks for the invaluable reply. That has almost got it right. However, whilst the team name is now where it should be, the remaining titles are still in a line across the top. Any ideas?? Thanks.

  46. Hi James, I am trying to my tables sorted as per your excellent scripts. However, it does not seem to be happening for me. Some of it works but other parts do not.

    One table consists of 8 columns with titles. 10 rows 9 of which are with team position number. the body of the table contains the data.
    The row of titles are not displayed along with the position number and data. It is in a long line across the top. You can see what I mean here:

    I have added the code to the Custom Header Code for the theme. I assume that this is correct. Any help greatly appreciated.

    • I took a look, and you will need to get the Javascript at the end of the page. Perhaps the theme has an option for custom footer code? If the Javascript is at the head, then the DOM elements have not yet been rendered for the code to manipulate.

  47. it works, very helpfull..thanks mate…

    • Too many colmtimenps too little space, thanks!

  48. Hey man you are the man!! I searched so long till i found your blog, easy and everybody can do it, thank you so much !!

  49. Thank you so very much for posting this! It was incredibly easy to implement and looks beautiful!
    You rock!

  50. It works great but it doesn’t break longer lines of text, so you still get a horizontal scrollbar on very small screens (smartphones).

    Any idea how to get the text to break into several lines?

    • Could apply word-wrap: break-word; to the TD

  51. Jquery not working, no errors but cannot view the change specified. can you help.

    Need a help ASAP

  52. Getting a wildly different result and can’t for the life of me figure out what I’m missing. Inspecting via Firebug to see if some of my theme CSS is messing up yours, but can’t locate anything. Any ideas? Very much appreciated as your example does EXACTLY what I need — IF I can get it to work!

    • Doesn’t look like all the CSS and JS has made it to your page (I just had a quick look).

  53. I’ve replaced the line:

    headertext[i].push(current.textContent.replace(/r?n|r/,””)); with


    And all seems OK now. I’m not sure why it needs to be there but then I’m not so good at regular expressions, so if you can explain that would be great

    • The regular expression removes any CRs (carriage returns) or CRLF (Carriage return line feed) from your markup. Sometimes, say if you are minifying HTML you might end up with some spurious linefeeds in your markup.

      To be honest it probably doesn’t need to be there unless you have this in your code. If it was removing actual letters from your HTML then it must somehow have been messed up during the copy and paste process.

      The n (backslash n) means it’s an escaped character – rather than an actual “n”.

  54. I’ve getting a similar issue to Marcus, in that some letters are removed from the table headers. It seems to be letters ‘r’ and ‘n’ for some reason. I’ve copied and pasted the script exactly as it is here. I can’t provide a url as it’s a private site, but here’s a link to a screenshot that illustrates the issue:

    • Michael, that worked for me. Thanks!

  55. James, this is great & thank you for sharing! I have a question on implementing it. Everything works well on my site, except that the “data-th” field on the mobile version cuts off the last letter of my item.

    It should read “Caliper” & instead just reads “Calipe”. If you have any thoughts/advice on this, it would be much appreciated.

    I can send the URL to the site if you need it…just didn’t want to put that into this comment in case it gets edited out.

    • Are you sure the code is copied exactly? I’m thinking the regular expression when it does the replace could have been messed up. Feel free to post your URL

      • Hi James, I think that was it…an issue with the regular expression replace function. I used Michael’s modified code below & it seems to be working well.

  56. Awesome job, works perfectly and easy for a client to use

    • Robert, try removing out all the ‘data-th’ attributes from your HTML. And… it should all still work.

      The idea of this code and script was so that you did not need to go thru all your markup and add in all the column headers. The javascript will go thru and pull that out for you from your headers.

      Less markup = less time and chance for errors.

  57. Hi I’m Bayanda , I just want to ask which plugin did you use for your
    contact form?

    thank you.

  58. Wouldn’t it be better to actually use pseudo css ::before/::after to get those headers to show up in each cell?

    I’m trying to figure a downside in doing this instead using js but i’m pretty in blank here 😐

  59. Get responsive menu here

  60. James,
    Thank you for posting this solution. I am trying to adapt this to the Hueman Child theme in WordPress. Can you pass on any pointers for incorporating this solution? I am particularly unsure how to add the script.
    Thank you.

    • Are you talking about adding JS to child themes in general?

      • Thanks, James. I am trying to get your code loaded properly on a page. I can get it to work if I use a plugin that adds the script to the footer, but I wanted to add it without using an additional plugin.
        I am trying to use wp_register_script and wp_enqueue_script as my understanding is that is the preferred way to load javascript.
        Eventually I want to use a shortcode to run wp_enqueue_script, but for now I have it set up as follows in the child theme’s php file:

        /* ————————————————————————- *
        * Custom functions
        /* ————————————————————————- */

        // Add your custom functions here, or overwrite existing ones. Read more how to use:
        function register_my_scripts() {
        wp_register_script ( ‘tablechanger’, plugins_url( ‘/js/tablechanger.js’, __FILE__ ), array(), ‘1.0’, true );
        wp_enqueue_script ( ‘tablechanger’ );

        add_action( ‘wp_enqueue_scripts’, ‘register_my_scripts’ );

        /* add_shortcode ( ‘tablechanger’, ‘tablechanger_handler’ );
        function tablechanger_handler() {
        wp_enqueue_script ( ‘tablechanger’ );
        } */

        The script gets registered, but it doesn’t run.
        Do you have any suggestions?

        • I got it to work. I had to change plugin_url to get_stylesheet_directory_uri

Add a Comment