Sorting Dashboard Course Cards

Community Champion

December 8, 2018 - No Longer Needed

Canvas has released their own solution for sorting the dashboard course cards. You should remove this script and use their solution. Things should continue to work until you do, but the effort is being duplicated. Any customization to the sort order will be lost when you remove this script.

I'm leaving the documentation and script out here for institutions that self-host and have not updated to the December 8, 2018, release.

Original Documentation

I've written a script that will allow users to sort their dashboard course cards by dragging and dropping the cards into the proper order.

This seems to have been a popular request that Canvas has repeatedly said will take too much effort to develop.

There are probably others that I'm missing, but that's a quick list.

The script was written as a user script, meant to be used by Greasemonkey with Firefox or Tampermonkey with Chrome or Safari. However, the exact same code can also be used in the custom JavaScript file for an institution.

What it does

The script runs on the dashboard for your Canvas installation. It checks to see if the user has sorted the course cards and arranges them in the order the user has specified.

If there is more than one course card, it allow the course cards to be sorted using a drag and drop capability. When cards are dragged to their new location, the current sort order is automatically saved.

If new courses are added to the favorites through the Courses navigation item, they are placed at the front of the existing course cards. The user will need to move them if they don't want them at the front.

The user's sort order can be removed and the ordering reset to the Canvas default by removing all but one course from the favorites and then loading the dashboard.


The process is so simple and straight forward that coming up with a video that demonstrates what it does was challenging. I managed to stretch it out to 15 seconds.


As an institution for all users

If you are a Canvas Admin and would like to install this script in your Custom JavaScript file for the entire account, then obtain the source code from the Canvancement site.

The file is called dashcard_sorter.user.js. Normally, the *.user.js notation is reserved for user scripts, but in this case the code will work as either a user script or a custom JavaScript, so rather than create two identical files, I just used the one. You do not need to include the user script metadata (all the lines starting with //) at the top of the file.

Add this code to your existing custom JavaScript and re-upload it to your theme. Then save and apply the theme. I added it to the global JavaScript file as well as the global Mobile JavaScript file, although I'll admit I'm not clear on what the Mobile one does.

Of all the scripts I've written, this is actually the first one that we've installed in our custom JavaScript. It has been tested with the latest versions of Firefox, Chrome, Safari (Mac), and yes, even Internet Explorer and Edge.

As an individual for yourself

If your institution won't install this for all users, you can still use it by installing it yourself. If your institution later installs the script for all users, you will want to remove the version you installed.

Note that you will need to install this script on every browser that you want the course cards sorted.

The quick installation instructions are:

  1. Install a browser add-on: Greasemonkey for Firefox or Tampermonkey for Chrome/Safari.
  2. Install the Dashboard Card Sorter script

If your Canvas site has a custom domain that does not match *, then you will need to modify the // @include line to specify your Canvas installation.

Canvas apps aren't supported

I have tested this in various browsers, including Safari on an iPhone. It has worked in all those browsers that I tested.

However, it did not work within the Canvas iOS app and I can only assume that it will not work in the Android app either, but I don't have access to test it.

I don't know if this can be remedied or if we're just out of luck. I'm happy to hear from someone who is more knowledgeable with the apps and can tell me how to make it work or who can confirm that it never will.

Behinds the scenes (technical -- optional)

The script uses the jQuery sortable method, which enables the drag and drop feature. jQuery is included on each page by Canvas, so no extra libraries were needed. I used the browser's console to quickly add a .sortable() to the dashboard cards as a proof-of-concept before starting the project in earnest. That part was quick and relatively painless.

The first hurdle was waiting until the course cards were ready before acting on them. The course cards are available as an empty shell that is available as soon as the page is loaded, but all of the content of the cards is loaded after the initial load using JavaScript. That makes determining which course is associated with the card impossible until the cards are filled out. I guess that I could know the order of the cards based off the array that is included in the ENV.DASHBOARD_COURSES variable when the page is loaded, but I wasn't sure what else was going on behind the scenes so I decided not to risk manipulating it until Canvas had added the content. I used a MutationObserver to wait until the cards could be associated with a course and then begin the processing.

I've done a lot of work with MutationObservers with other scripts, so this was not as difficult as it seems. You just need to watch for the right thing to happen. It turned out that the first change that was made to the dashboard cards was sufficient to start the process.

A second hurdle was saving the sort order. When I originally contemplated this project, I thought about saving it using the local storage for the browser. That would have worked for people who use the same machine and browser every time, but it would not work for people who move around between computers or change browsers.

The solution to that problem was to use the Canvas User Custom Data API. It stores the preferred sort order within Canvas so that it can follow users from machine to machine and browser to browser. If an institution doesn't install the script in their global JavaScript, the user will still need to load the user script on the machines, but then they won't have to reconfigure it each time.

This was the first time I've used the custom user data, so I had to learn how that works. Here is some advice for those considering it:

  • You need a namespace to separate your applications data from possible conflicts. Canvas recommends a reverse domain name, but I don't have a domain name for my Canvancements, so I just used "canvancement" as the namespace.
  • You also specify a scope, which is a way to have multiple values within your namespace. I chose "/dashboard/order". These are specified near the top of the source code and you are free to modify them to something else.
  • The actual data is stored under the field "data" within the namespace and scope. So, if you're expecting a comma separated list of course IDs, you don't just get that, but you get a JSON object with a property called "data" that contains the comma separated list of course IDs. That's okay once you know that's what it's doing, but I was expecting it to work like a cookie. Once you understand it, it makes sense, but it either wasn't clear or I missed it in the documentation so I mention it here.
  • While developing this as a user script, it worked fine, but when I went to the global custom JavaScript file, my data was getting returned with a while(1); followed by the JSON object. I've seen that before when typing an API call directly from the browser, so I knew what it was. The trick to get rid of it was to force the dataType to be json. That can also be done by using the jQuery.getJSON() method instead of the jQuery.get() method.

A third hurdle was the timing issue. AJAX calls are asynchronous and so I had no idea whether the call to load the custom sort order would be done before or after the MutationObserver had detected it was ready to start sorting. I ended up using a couple of scoped variables to check and when the custom sort order was done loading, it checked to see if the cards were ready. The call that happened when the cards were ready checked to see if the sort order had been loaded. By the way, the sort order may not ever be loaded and won't be the user has never sorted the cards.

Community Explorer

Hi James,

Thanks for your work. I'd love to do this as admin but am new and stuck. I think I need to:

add the code from dashcard_sorter.user.js file into your global JavaScript file.

I don't know how to do this. Where is my global JavaScript file? I see an option to upload a file under Themes. Do I just upload the dashcard_sorter.user.js file there? Even if this is what I am supposed to do, how do I get that file? I see the code when following the link, but don't know what to do with it. Do I copy the parts that are not commented out and paste them somewhere? Or do I save that as a .js file and upload it to the themes section? Or something else?

Kind regards,


Community Champion

 @kevin_jackson ,

What you do depends on whether or not you already have a custom JavaScript file. It sounds like you probably don't, unless someone else put it in there for you. If you do have an existing file, you'll need to add this code to the existing file rather than creating it from scratch.

Save the dashcard_sorter.user.js file on your hard drive. I would rename it as something like dashcards.js. That's just me being extra cautious. Tampermonkey looks for files loaded with a .user.js extension and I don't know if it would pick up something like this or not, so it's safer to just not give it the chance.

You can remove the metadata at the top. Those are the commented out lines that tell Tampermonkey what to do. They are not needed with a global JavaScript file. They won't hurt, either, since they're comments, but they're not necessary and add a little bit extra that you send to each person every time they use Canvas (at least it is cached by the browser).

On that upload screen, you then upload the JavaScript file from your hard drive where it says JavaScript. You don't need to put anything into the CSS upload. Do NOT put it in mobile, it doesn't work there.

Then preview your changes, save the theme, and apply it. Canvas' documentation on how to do this is in the Canvas Admin Guide: How do I upload custom JavaScript and CSS files to an account? 

Community Explorer

Hi James,

Thanks for that, I uploaded it and it works as it should. I'm really pleased!

A question regarding the javascript file: now that I've uploaded something, does this mean that I now have a custom js file and I would need to modify that if I wanted to make further changes in the future for other things? Where do I find this file?

Again, thanks so much for your work, it's much appreciated!

Kind regards,


Community Champion

 @kevin_jackson ,

Yes. The documentation and all of the warnings that come with custom global JavaScript are in the Canvas Guide How do I upload custom JavaScript and CSS files to an account? 

The best way to do it would be to keep it somewhere on your end and manage / revise it. Then you upload it back into Canvas when you need to make changes. If you ever lose that file, you can go to the Theme Editor and click the "View" button next to where the global JavaScript file is specified (That's explained in the "Manage Files" section of the document, about 75% of the way through). But you have to make any changes on your machine, save them, and reupload them to Canvas. There is no JavaScript editor inside Canvas.

I would also suggest uploading to your beta instance, but I saw in another thread you were having problems accessing that. Once you get that figured out, definitely test in beta or test so you don't bring down the whole institution with bad code.

Community Explorer

Thanks so much James, I really appreciate your advice!

Community Champion

Hi James:

I must be the slow one in class.

1. I went to your directory on Github and copied the script.

2. I opened Tampermonkey and copied the script.

3. I modied the URL to "" (I wanted to test it there first)

4. The script appears to be enabled, but I see no changes on the Dashboard.


Community Champion


It sounds like you made the installation a harder than you needed to, so I hope it is installed correctly. You really just click on the link to install it and it recognizes and installs it for you.  You can check that it's installed by looking at the Tampermonkey icon in the browser. It should have a badge (number in red on it). If it doesn't, it's not running any scripts. If it does have a badged number, then click on the icon and you should see something like this:


The red around the Dashboard Card Sorter is mine, just to highlight it. If it's not enabled, you'll need to enable it.

I don't have an account on and I don't run the script through Tampermonkey since our site has it installed as a global JavaScript. Every other Canvas site I have access to only has one course in it, so there's nothing to drag. I went into our beta instance to disable the JavaScript in our theme, but since it was Saturday, it was being updated and down. I went into test, removed the global JavaScript so I know it's running my version.

It worked for me.  I don't know if allows people to save custom user information, so that might be a difference. I don't really have a way to check that out.

This sound silly to ask, but did you try dragging & dropping the dashboard course cards to a new location? It doesn't do anything unless you do something first. You'll also need to be on the course card view, not the new To Do list or the activity list. There also needs to be more than one course.

Finally, Canvas has recently (since InstructureCon) marked the two feature requests  and  as "In Development", so we may actually see progress from them.

Community Champion

More silly questions...

In the instructions you indicate that the * needs to be changed. That's a comment line. The only way to change it is to go to your source code on Github > copy/modify the script and deploy it from Tampermonkey. Clearly, I'm missing something. I see the click to install option, but I wanted to test it on something other than our production instance. Similarly, I don't see a place to add our URL in the script. Attached is what I see when I open Tampermonkey.

The card drag and drop is not working on

Maybe I'm overthinking this, but I'm lost.


Community Champion

This is what I changed on your source code:

/ ==UserScript==
// @name        Dashboard Card Sorter
// @namespace
// @description Sort dashboard course cards using drag and drop
// @include   <<<<<<<<
// @version     1
// @grant       none
// ==/UserScript

Community Champion

The instructions say that if you're running on a site that does not match *, you will need to change it. The * is a wildcard, so fits the pattern, so you do not need to change it. It doesn't hurt if you do, but the * allows for things like as well. In other words, you could have done the quick install and not needed to change anything. When you do the quick install, it shows you the code and lets you make changes then, so you didn't have to do the copy/paste routine. However, if you don't want to run it on your production instance, then you would either need to change the include line or stay out of your production site while testing it. Even if you tested it on production, the user script version will only affect your account and nobody else's.

In what you quoted above, I hope there's a double slash at the very beginning. I don't think you would get the script to show up as enabled if it wasn't, so it was probably just a copy/paste issue here.

The Tampermonkey pull-down where it says "No Scripts Running" means nothing since you're on the Tampermonkey dashboard screen. You need to go to the main page for and then take a look at it.

As for testing whether you have permissions to write custom data on, you can take a look at the developer tools in your browser. On the page, hit F12 to bring up the tools. Then go to the Network tab and reload the page and try moving cards around. See if there are network calls to order


A 400 error on a GET will happen if you haven't set the order yet, so it may not mean anything, but a 4xx code on a PUT means something went wrong.

If you can't even drag the cards at all, then something else is going on. In this case, go to the Console page of the developer tools and look for error messages that might be related to the issue.

Community Novice

James, one of our staff today asked if it was possible to sort their dashboard, so I stumbled across your post. This solution worked perfectly. I had no idea about the user API having this ability to store custom_data fields. I can see all kinds of possibilities with that. Thanks a lot for sharing this.

Community Explorer

Thank you so much for sharing this!

Community Participant

Just wanted to add my thanks - we've been using this for a year and it works perfectly!

Community Participant


Thanks for writing this excellent tool! We've had it installed in our custom Javascript for over a year. However we've recently seen strange behavior:

One issue is that the dashboard cards can be dragged after initial logon to Canvas, but if you navigate away from the dashboard and then return, the cards can no longer be dragged around. This is happening across different browsers.

Another issue is that when the Dashboard loads, the cards will sometimes keep the custom ordering that the user has made, and sometimes not. I've seen the page briefly show the cards in the custom order, but after the page has completely finished rendering, the cards are in the default Canvas order. 

Have you encountered this issue before?

Community Champion

 @tcs1 ,

I have not encountered that behavior. Or, trying to maintain a growth mindset, I guess I should say that I haven't encountered it yet.

I just double checked our installation in Chrome and Firefox and it's working properly for me. That of course could just be falling into the "sometimes it works and sometimes it doesn't" category. Is your school doing anything else with the dashboard with custom JavaScript? Are you using a custom jQuery or jQuery UI for other things that might be causing an issue? Have you recently turned on any new feature flags to implement new things? Have you recently implemented a proxy server (so far the only people I've heard of with this problem is Indiana University and that was on a different issue, but you never know)?

One thing you can do that might shed some light is to open up the Developer Tools in the browser (normally F12) and switch to the network tab. Then reload the page and drag things around. If there is bad code in the custom JavaScript, it might be throwing an error and then never getting to the script. Look for calls to order. That is is either a PUT or GET and if they're failing then that suggests a problem that a proxy might introduce. If those are successful, then maybe something is changing in the underlying structure, which would suggest a recent change on someone's part. Canvas just updated over the weekend on September 15, so it is possible that something changed. I have been super-swamped with things at work and haven't been following the Canvas release notes as closely, so something might have slipped by. If you see something in the release notes that sound like it might have impacted this, please let me know.

Can you let me know if you find something out that might be causing it or make a screen capture of the behavior? You can direct message me through the Community with a link if you like or email me at the address you get when you mouse over my name. I'm still swamped, so the more people can help me out with information, the more I can try to track it down.

Finally, now that I'm done writing, it seems that carroll-ccsd‌ mentioned something the other day about $ missing. I think it might have been an error that showed up in the browser console. I didn't make time to dig into that fully to see what it was about, but if the script can't find $ (the shorthand for jQuery), then it wouldn't run since it relies on jQuery to do the AJAX calls. In that case, you wouldn't see any calls to order in the network tab of the developer tools, but you might see an error in the console tab. 

In a strange way, it would make me absolutely giddy if we were to find out that jQuery wasn't available to our scripts. When Canvas was deprecating jQueryUI, they kind of promised that jQuery would always be there for us before our scripts ran, but I've suspected that it was more an issue of timing and that it may not always be. That would certainly foster an exciting discussion between Robert and I about the best way to deal with that a stronger reason to switch to pure JavaScript, even though it is sometimes more cumbersome. I can make the AJAX calls with window.fetch() and I've been moving more and more stuff to that, but when I wrote the script I didn't know about it.

As it is, I'll try to get caught up on work that has gotten backlogged and wait to see if you turn up anything from the developer tools when it's not working.

Community Novice

We have only had this installed for about a month, but now it is broken in Canvas, our users are already missing it! If you could update it to work again, I know a lot of people would be very happy!

Community Champion

 @joel_mills  ,

It's still working for me so I'm not sure what needs fixing. Can you read through my post from September 20 and try some of the debugging things I mentioned or provide more information? I think the person who mentioned it before direct messaged me and said they were doing a lot of custom things and it was probably one of their scripts messing with things. 

I briefly looked into it and it appeared that I was just waiting for the first mutation to start acting on things and then disconnecting the mutation observer. But if Canvas is rewriting things or if you have other code manipulating it, then mine isn't going to pick it up. We don't do have any extra JavaScript in our instance and I'm not sure exactly what people are doing that breaks it, but as of right now, I'm not able to reproduce the behavior.

Community Champion

I am not seeing any issues either, used within in our global theme with all the other stuff we have. Also works in test, pasted into the console, so I don't see any breaking changes.

I am no longer seeing the warnings I saw a few weeks ago in the browser console about $ and jQuery.

Community Novice

 @James ‌

Is it possible to adjust this script to have a longer long-press delay before the tiles are actually draggable? I have reason to suspect your script is installed in my institution's Canvas because tiles are definitely rearrangeable but it causes issues on touch-screen devices because the cursor tends to move ever so slightly when just 'tapping' the tile. It forces us (me) to repeatedly press the tile until the cursor happens to not move when I tap the tile. Quite annoying.

Let me know!  

Community Champion


Thanks for bringing this up. I have had a few students with the issue on some phones like you described, but that's just me hearing from a few students that happened to be in my classes and haven't heard a mass revolt because it seems to work for most people. You didn't say which touchscreen device you were using, but I couldn't track down a single issue with my students, except that it looked like some were using non-supported browsers or certain cellular carriers that had restricted what their browser could do.

I looked into the issue before and found that the delay option was deprecated in jQuery UI 1.12, and they said in the upgrade guide that interactions should be instantaneous and that the UI should be improved such as adding draggable handles. That would mean that only a small portion of the card would be draggable and that I would have to mess much more with the card than just making it sortable(). That would be the right way to do things, but it is also more likely to break.

So newer (since 2016) version of jQuery UI have deprecated it. Back in 2012, it was stated that there was basically no way to do this without rewriting jQuery UI or adding your own bind() or on() features.  I'm not sure what version of jQuery UI Canvas is running, but I don't see the delay option in there. Even if it was there, I wouldn't add it because of the reason it was deprecated.

Canvas has marked this idea as under development, so I'm hoping that it will be obsoleted sooner rather than later. Shortly after I wrote it, they came out with something so I thought it would be short-lived at that time. That was a couple of years ago now. But they have also said that they are deprecating jQuery UI, so we should start looking for other JavaScript libraries that provide the functionality. In other words, we shouldn't develop for jQuery UI unless we plan on loading the library ourselves. The reason I used jQuery UI when I wrote it was because it was available and exposed as part of Canvas, but I'm not sure how much further development should be done using it.

At this point, I'm not sure of the best way to fix it in a permanent solution that doesn't run the risk of breaking quickly as Canvas changes things because I've messed too deeply within the internals of the dashboard card. If it breaks for everyone, then few will use it. So far, I've heard of limited issues that have (again so far) been limited to certain issues with the browser the person was trying to use. I don't have a cell phone or tablet to test this on where the problem occurs.

It's not the ideal solution, but you might see if you can modify your workflow to head to the Courses menu first rather than clicking directly on the dashboard or perhaps setting your dashboard to the new list view so you're not tempted to use the dashboard course cards.

Community Novice

Thanks for the reply James!

For clarification, I'm using a Microsoft surface running Chrome. No issues on mobile though.

Thank you for the input, I will have to try the method you suggested.


Bradyn Claycomb | EMET Maj.

CAB Vice-president

Outdoors Club Treasurer

PSNK THON Asst. Media Chair


Community Novice

I'll take another look and have a try. We don't have very much custom JS at all, so I can't see this being an issue. Also, I don't understand as it did work, and now doesn't but we haven't changed the JS file??

Anyway, I'll take a look and see if we can get it to work again.


Community Novice

OK.. so its something to do with a difference in our Production Environment compared to our BETA environment. Same .JS file used in both and it works fine in BETA, but no JS working in production. 

At #CanvasCon today so will reach out to  @soberoi ‌ to take a look at our environments and see why JS is not working in general in the theme.

Community Explorer

I've noticed about a half second delay on page loading where the original course card order is shown before the re-ordered cards replace it. Is there any way around this or is that just the nature of using JS?

This was in chrome.


Community Champion


I have to wait until the content is placed on the page -- Canvas does this dynamically, not sent with the page itself -- before I can manipulate it.

Community Champion


Canvas has announced that they will have a native solution for this: Canvas Release Notes (2018-12-08) .

While my script seems to work okay with what Canvas supplies, it becomes obsoleted, and can be removed after the December 8 update. 

The two approaches are not compatible, so users will have their course order reset one time and have to reorder them.

I could probably write code that would go through and save my ordering into Canvas' system and then remove the custom data that I had in place. I'm not sure how many institutions would want to install that clean-up script, though, and I don't have a lot of free time right now to work on it.