It’s a common problem. You have a partial view, let’s say a carousel, with an associated javascript file. You want to include it on multiple pages of your site. The usual solution is to include the script in a @section scripts { }, but you can’t do that in a partial view. What to do?

The simple answer is to remember to include the script in the appropriate page view files, but I’m an extremely forgetful person, liable to not do this and then spend half an hour trying to figure out why my component isn’t working… Not ideal.

If we wanted to stay in familiar territory, we could keep using the scripts section, nesting them for each view. This means that the layout and partial files can render or use the section as normal, but all intermediate files would need to include the following:

@section scripts {
    @RenderSection("scripts", required: false)

Again, not ideal. Plus I’ve not actually tested to see if this works.

While I was working at Enjoy Digital, I came across an alternative, which I’ve refined since. At its simplest, it uses the HttpContext to store a list of scripts to output in the layout. The HttpContext stores all information related to the active request, like the user’s cookies and the active session. This means we can also use it to store any scripts that we may need to output.

public enum ScriptPosition
public static class HtmlHelperExtensions
    private class RequiredScript
        public string Source { get; set; }
        public IHtmlString RawCode { get; set; }
        public bool Async { get; set; }
        public bool Defer { get; set; }
        public ScriptPosition Position { get; set; }
    private static IList<RequiredScript> Scripts
            return HttpContext.Current.Items["RequiredScripts"] as IList<RequiredScript> ?? new List<RequiredScript>();
            HttpContext.Current.Items["RequiredScripts"] = value;
    public static void RequireScriptUrl(this HtmlHelper html, string url, bool async = true, bool defer = true, ScriptPosition position = ScriptPosition.BodyEnd)
        var scripts = Scripts;
        if (scripts.All(s => s == null || !s.Source.Equals(url, System.StringComparison.InvariantCultureIgnoreCase)))
            scripts.Add(new RequiredScript { Source = url, Async = async, Defer = defer, Position = position });
            Scripts = scripts;
    public static void RequireScriptCode(this HtmlHelper html, IHtmlString code, ScriptPosition position = ScriptPosition.BodyEnd)
        var scripts = Scripts;
        if (scripts.All(s => s == null || !s.RawCode.ToString().Equals(code.ToString())))
            scripts.Add(new RequiredScript { RawCode = code, Position = position });
            Scripts = scripts;
    public static IHtmlString RenderScripts(this HtmlHelper html, ScriptPosition position)
        var builder = new StringBuilder();
        foreach (var script in Scripts.WhereNotNull().Where(s => s.Position == position))
            if (script.RawCode != null && !string.IsNullOrWhiteSpace(script.RawCode.ToString()))
                builder.AppendLine("<script>" + script.RawCode + "</script>");
            else if (!string.IsNullOrWhiteSpace(script.Source))
                builder.AppendLine("<script src=\"" + script.Source + "\"" + (script.Defer ? " defer" : null) + (script.Async ? " async" : null) + "></script>");
        return new HtmlString(builder.ToString());

That’s a lot to unpack! Let’s go through it bit by bit. Firstly we have the ScriptPosition enum, allowing us to define the different parts of the HTML that scripts can appear in. We could add additional members to this, but these three are the only ones I’ve needed so far.

We then have the RequiredScript class. This simply defines what a script consists of, so we can store them easily. Its properties allow us to track what scripts should have the defer and async attributes, as well as their intended position in the final HTML.

We define a property for easy access to our HttpContext item. The string value doesn’t particularly matter, as long as we use the same one throughout and it doesn’t clash with anything else. Feel free to change it if needed.

What follows are two extension methods to the HtmlHelper, one for including scripts with a particular URL, the other for raw code. We would use these wherever we want to include our scripts, so in views, partials, even controllers! For our carousel example, we can include the script directly from our partial view:

@Html.RequireScriptUrl("//[email protected]/slick/slick.min.js")

Finally, we have a third extension method for outputting our stored scripts. This is called where we want to render the scripts associated with each position, usually the layout file. Mine looks something like this:

<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Sharper Pencil</title>
    <link href="/assets/css/style.css" rel="stylesheet">
    <div class="main-content">
    <script src="/assets/js/common.js"></script>

Because the layout file is rendered last, it doesn’t matter where we’ve included the scripts from, they will always be rendered in the appropriate position.

For an added bonus, we could prioritise our scripts by adding a Priority integer property to our RequiredScript class, and a similar parameter to the appropriate extension methods, then ordering by this in the render method. This would mean that if a script has any dependencies, we can ensure that the dependencies are loaded first by giving them a higher priority.

We could also use similar methods to render anything else into our layout file - CSS, meta tags, even additional HTML snippets! All we'd need would be the appropriate Require and Render methods, using the HttpContext.Current.Items dictionary to store everything. I'd recommend not going too crazy with this though; only use it if you have to!

And that’s it! No more forgetting to include the appropriate scripts for each partial in every single view, and no more hacky workarounds (except this one). You can finally associate your component directly with its scripts!

Rachel Drake

.NET web developer from Leeds (UK), specialising in both the Umbraco and Sitecore content management systems using ASP.NET MVC. Rachel loves experimenting with code, learning new things, and doting on her two cats.

Proudly sponsored by


  • Moriyama build, support and deploy Umbraco, Azure and ASP.NET websites and applications.

  • CI/CD service for Windows, Linux and macOS
  • Build, test, deploy your apps faster, on any platform.

  • is the easy error logging and uptime monitoring service for .NET.
  • Take back control of your errors with support for all .NET web and logging frameworks.
uSync Complete

  • uSync.Complete gives you all the uSync packages, allowing you to completely control how your Umbraco settings, content and media is stored, transferred and managed across all your Umbraco Installations.

  • More than a theme for Umbraco CMS, take full control of your content and design with a feature-rich, award-nominated & content editor focused website platform.

  • Affordable, Geo-Redundant, Umbraco hosting which gives back to the community by sponsoring an Umbraco Open Source Developer with each hosting package sold.