In this article we will use our bundle on an actual page. We will add more JavaScript and styles to the bundle. We will also use a very usefull windows command line utility called robocopy, it is much more advanced than xcopy and can simplify lots of your every-day development&deployment tasks. The good thing about Office 365 sites, is that they can be mapped as network drives, so we can use robocopy or any other console utility to interact with files and folder on a site in an old way, just like we did with SharePoint on-prem. Unfortunately some of the system lists are not availiable when using WebDav, for example Web Part Gallery. In this case you will have to fallback to powershell or CSOM. Also, it is possible to use SharePont Online REST interface to interact with different server-side objects. It is a more advanced topic, that I won't cover in this article.

Using our bundle with CQWP

  1. Make sure you have a mapped network drive that points to your Office 365 dev site. You will need to upload bundle created on a previous step to Style Library\Custom\Demo\Bundles. Just execute a following command:
    robocopy "." "Z:\Style Library\Custom\Demo\Bundles" "bundle.js"     

It will copy your bundle.js file to a target folder, it will also create that folder if it doesn't exist. We will create a more advanced deployment script latter, that will update both *.js and *.xsl files. More information about robocopy command can be found at TechNet

  1. Open ContentQueryMain.xsl, go to line ~46 and apply following modifications:
    <xsl:text disable-output-escaping="yes"><![CDATA[<script type="text/javascript" src="/sites/dev/Style Library/Custom/Demo/Bundles/bundle.js"></script>]]></xsl:text> 

It should look like this, make sure you closed the script tag, otherwise XSLT rendering will fail:

  1. Go to the page and check browser console, it should show you the following message:

Optimizing development workflow

Now when we know that our bundle and xslt works, it is time to optimize our development workflow. It would be much easier to make changes if we can pack&deploy all assets using a single command, lets say go.

  1. Execute the following in your VS code terminal remove an old version of bundle.js, we will use a different folder to store our bundle.
    del bundle.js
  1. Update the webpack.config.js:
    module.exports = {
        entry: "./entry.js",
        output: {
            path: __dirname,
            pathinfo: true,
            filename: "./artifacts/Bundles/bundle.js",
  1. Now you will have to manualy copy everything from Z:\Style Library\Custom\Demo\ to 'artifacts' folder. You should get something like this:

  2. Create a new file go.bat and copy the following content into it:

    @call webpack
    @robocopy "artifacts" "Z:\Style Library\Custom\Demo" *.* /MIR

So now, when we have go command in our root directory we can easily copy our xslt and js files to target folder at Office 365:

    Hash: 0a0318058093b180655c
    Version: webpack 1.13.1
    Time: 43ms
                            Asset    Size  Chunks             Chunk Names
    ./artifacts/Bundles/bundle.js  1.5 kB       0  [emitted]  main
    [0] ./entry.js 37 bytes {0} [built]

    ROBOCOPY     ::     Robust File Copy for Windows

    Started : Sunday, August 21, 2016 4:39:46 PM
    Source : c:\Projects\News\artifacts\
        Dest = Y:\Style Library\Custom\Demo\

        Files : *.*

    Options : *.* /S /E /DCOPY:DA /COPY:DAT /PURGE /MIR /R:1000000 /W:30


                            0    c:\Projects\News\artifacts\
            *EXTRA Dir        -1    Z:\Style Library\Custom\Demo\Bundles
            *EXTRA Dir        -1    Z:\Style Library\Custom\Demo\XSL Style Sheets
            New Dir          1    c:\Projects\News\artifacts\Bundles\
    100%        New File                1496        bundle.js
            New Dir          3    c:\Projects\News\artifacts\XSL Style Sheets\
    100%        New File               19324        ContentQueryMain.xsl
    100%        New File                2606        Header.xsl
    100%        New File                 883        ItemStyle.xsl


                Total    Copied   Skipped  Mismatch    FAILED    Extras
    Dirs  :         3         2         1         0         0         2
    Files :         4         4         0         0         0         0
    Bytes :    23.7 k    23.7 k         0         0         0         0
    Times :   0:00:10   0:00:07                       0:00:00   0:00:03

    Speed :                3266 Bytes/sec.
    Speed :               0.186 MegaBytes/min.
    Ended : Sunday, August 21, 2016 4:39:58 PM
  1. The complex part is over, after this you can do whatever you can do with the webpack and xslt.

  2. We need to add an another template to ItemStyle.xsl file to make it show something useful, for example a list of articles with some basic styling.

    <xsl:template name="news" match="Row[@Style='news']" mode="itemstyle">
        <div class="link">
            <a href="{@LinkUrl}" class="read-more"><xsl:value-of select="@Title" /></a>

Now you should be able to run go and update properties of CQWP on page, just select a new template, you have to see something like this:

  1. Add more webpack loaders to support our custom style sheets and javascript:
    npm i --save-dev css-loader
    npm i --save-dev style-loader
    npm i --save-dev jquery
  1. Add these lines to src/style.css:
    .link {
        background-color: deepskyblue;
        margin: 10px;
        padding: 10px;
        border-radius: 32px;
        width: 200px;
        text-align: center;

    .link a {
        color: white;
        font-weight: bold;
        font-size: 32px;
        text-transform: uppercase;
  1. Replace content of entry.js with these:

    var $ = require('jquery');

    $(function () {
        var wpId = $('.news').closest('div[webpartid]').attr('webpartid');

        console.log('WebPartId : ' + wpId);
        console.log('jQuery version : ' + $.fn.jquery);

This code will load style.css and jquery as dependencies, then it will show current webpart id (that can be used in CSOM calls latter) and a current version of jquery. It is very important to understand a major advantaje of using webpack here, jQuery will be used only inside the bundle it will not leak to the page. It is extremely useful feature, it allows you to use different version of the same library on the same page.

  1. Run go and observer the results:

The first call to jQuery will show a version inside bundle.js, the second will fail, because there are no jQuery in global scope.


  1. Download complete solution:

  2. Adjust go.bat to point to your Office 365 root site collection mapped drive

  3. Deploy .webpart file manualy

  4. Run:

    npm install
  1. Modify & Enjoy :)


I used this type of workflow on many projects, it allows me to blazingly fast implement and deploy different CQWP webparts to Office365. I found this workflow to be much more developer-friendly than using SharePoint designer.

I usually use VS code for such projects, but it is possible to use any good text editor or IDE. It is even possible to develop on a MAC or Linux box, you just need to replace the robocopy command and a bat file with corresponding equivalents. Take a look on rsync, it should be a viable alternative.

Unfortunately, because of nature of Office365, SharePoint and WebDAV it's not possible to copy only changed files and folders using a robocopy command. To do this, we will have to use powershell and .NET objects, I am not going to describe it here, but it is possible, you can find lots of info regarding this topic on the Internet.

The other option is to adjust go command to copy only bundle.js or only xslt depending on command-line parameters.

It is also possible to use a webpack plugins infrastructure to do some more advanced stuff, like uploading *.webpart files to the Web Part Galery using REST interface from Node.js. It can be done using a few simple http requests to a proper urls, refer to spo-auth to get started with SharePoint Online REST interface.