As with anything SharePoint there are many ways to get what you want. Depending on what type of solution (Farm, Sandboxed, App) you are working on some may have already been decided for you. From my own projects and experience I find there are several common locations for deploying such assets. In this post I’ll go through each of them and explain which one I’ve chosen and why.
For most common scenarios deploy assets in their own subfolder under Layouts in the 15-hive.
To start with these are the locations I usually see assets deployed to:
- SharePoint library
- Layouts folder
Lets go through them one at a time and then I’ll present my solution for asset deployment:
A SharePoint library may at first seem like the most obvious choice. It is easy to manage from a browser and provide many useful features such as versioning and permissions. The most common library for storing front-end assets is the Style Library, but any library may serve this purpose.
- Easily accessible from the browser.
- “Free” version control.
- Easy to update/change scripts.
- No server-side actions required.
- Must deploy and maintain multiple instances of the same scripts one for each site collection.
- No automatic revision tags.
- Must deal with ghosting/unghosting files when deploying on the server.
- Must set up blob-cache to ensure performance.
- Potentially accessible by “regular users”.
For some situations the Style Library is the best (and only) choice. It is readily accessible from the front-end without bothering the IT Pro, it comes with free version control and requires no build or deployment steps to upload to. However when deploying long-lived farm solutions that may contain 10-100 asset files this quickly becomes unwieldy.
The biggest issue with using a library is that you have to deploy the files over and over for each new site collection that is created. If you are working with a Publishing Site or something similar this might not be an issue, but for collaboration solutions containing multiple site collections this quickly becomes a hassle. The pro of having the files be easy to change quickly vanish once you have to upload the changes to 50 different site collection libraries each time. This is mitigated some by ghosting the file to disk, but you still have to ensure that no users have modified the files within their own site collection.
Another issue, and one that is almost equally problematic, is that of client-side caching and versioning. Browsers these days are incredibly good at getting pages to load fast. One of the features that helps accomplish this is client-side caching where the browser stores local copies of assets like JavaScipt, images and CSS so it doesn’t have to re-download them each time a page that uses them is visited. Since these types of files rarely change in most situations this is a good thing. The problem occurs when the browser has a local copy that is older than the version on the server. This can easily happen if you update the file while it’s in a library.
The caching problem is usually solved by altering the url of the requested script file based on its version. If you open the source view of a SharePoint page you will find several links that look like this:
Notice that the urls contain a rev parameter:
/_layouts/15/1033/styles/Themable/corev15.css?rev=ox%2BqLd6WTqhn6d%2FMqf2BMw%3D%3D /_layouts/15/init.debug.js?rev=QlAUd%2BXxZ3brvzueisei2w%3D%3D /_layouts/15/blank.js?rev=ZaOXZEobVwykPO9g8hq%2F8A%3D%3D
This is how SharePoint distinguishes between versions of files. The rev parameter contains a hash of the files content that changes whenever the content changes. Since the browser caches files based on their url this will instruct the browser that it needs to re-download the file whenever it changes. However for files stored in libraries there is no such mechanism. In my opinion this issue completely eliminates the convenience of having easy access to files for updates.
Performance is another thing where libraries may suffer. Files stored in libraries are stored in the content database of the site collection and must be fetched from there each time they are requested. SharePoint provides two mechanisms for mitigating this issue. One is ghosting. A ghosted file is a file that is stored on disk and at the same time “mirrored” in a library. As long as the library instance remains the same as the disk instance SharePoint can fetch the file directly from disk and not have to go all the way back to the database. For farms with multiple front end servers this means less traffic back to the database hence faster load times. However as soon as the file is modified from the UI (e.g.: a new version is uploaded) the file is unghosted and each new request for it must read from the content database.
The other way SharePoint improves performance for files in the content database is through the Blob Cache. This is a mechanism that caches files (or Binary Large OBjects) that are stored in the content database on the disk of the front-end server thus preventing multiple round-trips to the database when the same file is requested more than once. The Blob Cache also works for files stored in libraries so it will mitigate the performance issue, however it introduces the caching problem again, but this time on the server. A file that is cached in the Blob Cache can only expire after a certain time or if an admin explicitly tells it to. To make sure that clients always get the latest versions of your scripts you must flush this cache whenever a script changes. Since each front-end server maintains its own Blob Cache store you must repeat the task on each server. The UI provides a way for site collection admins to flush the Blob Cache however:
If we have multiple WFEs in the farm, each WFE will maintain its own copy of Disk-based Cache. SharePoint does not have a Web user interface (UI) to flush the disk-based cache on all the servers in a farm and neither is there an option to select a specific WFE.
The Layouts folder is known to most SharePoint developers. This is where Out-of-the-Box SharePoint features, scripts, pages and other assets are stored. At first you may be wary of placing your own files here and rightly so, but if you are careful there are several advantages to putting them here.
- No blob-cache necessary. File is already stored and served from disk.
- Out of reach of “regular users”.
- Discourages “quick-fixes” on files as they are deployed server-side.
- Must be deployed server-side.
- Lives with the SharePoint OOB files.
- Cannot be used with sandboxed solutions/apps.
Deploying to the Layouts folder almost immediately eliminate most of my issues with asset deployment to libraries. Files deployed here are stored on disk eliminating the need for them to be cached in the Blob Cache and support revision tags by using the <SharePoint:ScriptLink> and <SharePoint:CSSRegistration> tags. They are also not accessible by regular users preventing accidental modifications. In short this is the way I deploy my assets in all my projects, however there are some gotchas and issues that need to be addressed.
The primary reason many people will give for not deploying to the Layouts folder is that this is the location of all of SharePoints assets as well. You should never EVER modify the files that come with SharePoint, not even to fix bugs as changes here may have unintended side effects and they may be updated without warning in later patches. You can however add new files here as long as you take care not to overwrite anything.
To get your files onto the SharePoint server in the library scenario above you would simply upload them to the library the way you would upload any other file. This is not true for the Layouts folder. Here you will need to create a farm solution in Visual Studio and map the Layouts folder to it. Once you have added all your assets you must package and deploy your solution using PowerShell. This might scare some of you away, but it’s really not a big deal. I wouldn’t cover the deployment steps here, but if you want to know more go look up the Add-SPSolution and Install-SPSolution cmdlets). Deploying assets like this will take the server down for a short while so it should be done sparingly.
Structuring your assets
Once you have your folder you should create a subfolder for your project. I usually use my project name so that the resulting url will be something like:
I also create sub folders for scripts and css for convenience. Here is an example of a project structure with some assets:
If you are curious about the .scss file go check out SASS. It’s an awesome tool for “programming” CSS.
The result of this is that my assets are neatly stored within their own subfolders in the 15-hive and that they don’t touch any other assets. It’s also immediately obvious what project they belong to both when looking up the files in the 15-hive and when looking at the url for the asset.
Another arguably nice bonus of this deployment regimen is that all code is always located in the project and hence in version-control. With the library deployment method you do get versioning on assets, but it is maintained in SharePoint and different site collections may have different version histories for their files. Since everything here is in the actual project they are all maintained in one spot and you can easily browse their version histories.
Personally I also find it useful that there is some barrier to updating script files. As more and more logic is moved to the front-end it becomes vital that these scripts are tested properly before deployment. If the files can be directly modified in SharePoint it quickly becomes tempting to simply roll an update by uploading a “fixed” file. You might (read should) also have automated tests as part of your deployment strategy that uploading files directly may skip. This can be prevented by enforcing strict routines that require developers to run the tests before uploading a file, but for some the temptation may be too great.
For all of my current projects I’ve strictly adhered to the Layouts folder routine for deploying assets to SharePoint. It works well with the caching mechanisms, is fast and provides a strict separation between developed code and user-space. I fully acknowledge that there are some situations where this cannot be done or where it is better to deploy to a library, but in my personal experience these are few and far between (at least when working with farm solutions). In short I’d recommend this to anyone starting off with a new project.
Did I miss something? Is something unclear? Have I made a mistake somewhere? Feel free to comment below!