TipJar: Title Tags and Master Pages

asp.net, asp.net mvc 0 comments suggest edit

There are a couple of peculiarities worth understanding when dealing with title tags and master pages within Web Forms and ASP.NET MVC. These assume you are using the HtmlHead control, aka <head runat="server" />.

The first peculiarity involves a common approach where one puts a ContentPlaceHolder inside of a title tag like we do with the default template in ASP.NET MVC:

<%@ Master ... %>
<html>
<head runat="server">
  <title>
    <asp:ContentPlaceHolder ID="titleContent" runat="server" />
  </title>
</head>
...

What’s nice about this approach is you can set the title tag from within any content page.

<asp:Content ContentPlaceHolderID="titleContent" runat="server">
  Home
</asp:Content>

But what happens if you want to set part of the title from within the master page. For example, you might want the title of every page to end with a suffix, “ – MySite”.

If you try this (notice the – MySite tacked on):

<%@ Master ... %>
<html>
<head runat="server">
  <title>
    <asp:ContentPlaceHolder ID="titleContent" runat="server" /> - MySite
  </title>
</head>
...

And run the page, you’ll find that the – MySite is not rendered. This appears to be a quirk of the HtmlHead control. This is because the title tag within the HtmlHead control is now itself a control. This will be familiar to those who understand how the AddParsedSubObject method works. Effectively, the only content allowed within the body of the HtmlHead control are other controls.

The fix is pretty simple. Add your text to a LiteralControl like so.

<%@ Master ... %>
<html>
<head runat="server">
  <title>
    <asp:ContentPlaceHolder ID="titleContent" runat="server" /> 
    <asp:LiteralControl runat="server" Text=" - MySite" />
  </title>
</head>
...

The second peculiarityhas to do with how the HeaderControl really wants to produce valid HTML markup.

If you leave the <head runat="server"></head> tag empty, and then view source at the rendered output, you’ll notice that it renders an empty <title> tag for you. It looked at its child controls collection and saw that it didn’t contain an HtmlTitle control so it rendered one for you.

This can cause problems when attempting to use a ContentPlaceHolder to render the title tag for you. For example, a common layout I’ve seen is the following.

<%@ Master ... %>
<html>
<head runat="server">
  <asp:ContentPlaceHolder ID="headContent" runat="server"> 
    <title>Testing</title>  
  </asp:ContentPlaceHolder>
</head>
...

This approach is neat because it allows you to not only set the title tag from within any content page, but any other content you want within the <head> tag.

However, if you view source on the rendered output, you’ll see two <title> tags, one that you specified and one that’s empty.

Going back to what wrote earlier, the reason becomes apparent. The HtmlHead control checks to see if it contains a child title control. When it doesn’t find one, it renders an empty one. However, it doesn’t look within the content placeholders defined within it to see if they’ve rendered a title tag.

This makes sense when you consider how the HtmlHead tag works. It only allows placing controls inside of it. However, a ContentPlaceHolder allows adding literal text in there. So while it looks the same, the title tag within the ContentPlaceHolder is not an HtmlTitle control. It’s just some text, and the HtmlHead control doesn’t want to parse all the rendered text from its children.

This is why I tend to take the following approach with my own master pages.

<%@ Master ... %>
<html>
<head runat="server">
  <title><asp:ContentPlaceHolder ID="titleContent" runat="server" /></title>
  <asp:ContentPlaceHolder ID="headContent" runat="server"> 
  </asp:ContentPlaceHolder>
</head>
...

Happy Titling!

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

20 responses

  1. Avatar for Erik Porter
    Erik Porter April 2nd, 2009

    Or you could just remove runat="server" on the head tag. Found that to get rid of all the quirks. Didn't really find it necessary to be able to set the title in code with MVC anyway.

  2. Avatar for Web Design
    Web Design April 3rd, 2009

    Why wouldn't you just use <title><%=[SourtOfTitle]%> ?

  3. Avatar for huey
    huey April 3rd, 2009

    I never even noticed the head was set to runat="server".
    Is this how relative paths for script / links are kept up to date depending on the route?

  4. Avatar for Remmus
    Remmus April 3rd, 2009

    Although this doesn't work

    <%@ Master ... %>
    <html>
    <head runat="server">
    <title>
    <asp:ContentPlaceHolder ID="titleContent" runat="server" /> - MySite
    </title>
    </head>

    this does work

    <%@ Master ... %>
    <html>
    <head>
    <title>
    <asp:ContentPlaceHolder ID="titleContent" runat="server" /> - MySite
    </title>
    </head>

    Is there a reason you shouldn't do this?

  5. Avatar for Chris Porter
    Chris Porter April 3rd, 2009

    If you remove runat="server" from the head tag, it will cause ~ path resolution in the head block to stop working.
    This same problem (or one very close to it) existed and drove me nuts in Web Forms. What always drove me nuts was that the title tag in Web Forms renders the opening title tag, the content, and the closing title tag all on separate lines. This can cause issues with some SEO and while it might be "valid" it isn't desired. I really hoped MVC would be more flexible but it sounds like we still have to jump through hoops.

  6. Avatar for Erik Porter
    Erik Porter April 3rd, 2009

    @Chris Porter
    If you're building a medium to large site you'll probably want to control your urls anyway (and call a helper or something to make paths relative to your app or to your CDN or to a single crunched url for all your scripts or css or whatever is appropriate in your case) and then you'll have complete control over the markup in the head tag. You can still use placeholders like Phil mentioned, which is definitely useful.
    Nice last name, btw. ;)

  7. Avatar for Nebbercracker
    Nebbercracker April 3rd, 2009

    Another option is to use a global resource for the title of the website and then refer to the page name. so, in the master page:
    <title><%#Resources:Site,Title%> - <%=Page.Title%></title>
    Simple.

  8. Avatar for Andy
    Andy April 3rd, 2009

    Hey Phil,
    Love MVC and just really starting to get more into it - do you know if this works with MVC RC ? http://www.codeplex.com/MvcMembership
    Seems its stopped its otherwise active development ?
    Any help would be much appreciated!

  9. Avatar for someone
    someone April 3rd, 2009

    I've been using the following for a long time now with a lot of sucess... is there any reason why this shouldn't be used?
    <head runat="server" visible="false" />
    <head>
    <title><%=this.Page.Title ?? "Untitled Page"%></title>
    <!-- ... anything else here -->
    </head>

  10. Avatar for Stephen
    Stephen April 3rd, 2009

    Phil, in your final solution:
    <title><asp:ContentPlaceHolder ID="titleContent" runat="server" /></title>
    How would that be tweaked so it will show a default title if none is specified in the content page?

  11. Avatar for Erik Porter
    Erik Porter April 4th, 2009

    @Stephen
    Just put something in the ContentPlaceHolder in your master.
    <title><asp:ContentPlaceHolder ID="titleContent" runat="server">Default Title</asp:ContentPlaceHolder></title>
    If you specify a title in the content page, it's value will be the only one rendered. If you don't specify anything in the content page, then "Default Title" will be used.

  12. Avatar for Jeremy Caney
    Jeremy Caney April 4th, 2009

    My preferred approach (as indicated by a few others) is to use the Title property of the Page class which is exposed as an attribute on the <%@ Page %> directive. This allows you to centrally define the title and centrally reference it in your Master Page, WebForm, User Control, or (Partial) View. By default, the Title property will be rendered as the title in the <head RunAt="Server" /> control - although you can overwrite this using a <%= Page.Title %> statement (or a data bound equivelent, since you don't want a <%= %> statement inside an <asp:PlaceHolder /> since it will prevent overwriting the PlaceHolder). I was a bit surprised the OOTB MVC scaffolding didn't utilize this - although I suppose that despite being (slightly) more code it does offer more flexibility.

  13. Avatar for Joe Wulf
    Joe Wulf April 6th, 2009

    <head runat="server"> sucks.
    I would never find a reason to do such a thing.
    Thank god you forget <body runat="server">

  14. Avatar for Jack
    Jack April 7th, 2009

    with so many runat='server', will it hurt the performance badly?

  15. Avatar for Peter Lanoie
    Peter Lanoie February 18th, 2010

    Using VS2008 and MVC 1, the tag Phil suggests:

    <asp:LiteralControl runat="server" Text=" - MySite" />

    Results in the error "Unknown server tag 'asp:LiteralControl'."
    Instead use:

    <asp:Literal runat="server" Text=" - MySite" />

  16. Avatar for Johnny
    Johnny May 11th, 2010

    I get the same error:
    Unknown server tag 'asp:LiteralControl'.
    using MVC 2 RTM with Visual Studio 2010

  17. Avatar for Roger
    Roger June 1st, 2010

    Thanks for the Title Literal tip Phil!
    BTW, for .NET 3.5+ It's <asp:Literal /> not <asp:LiteralControl />

  18. Avatar for Andrew
    Andrew September 12th, 2011

    Good advice Phil, worked a treat. Now to find how to do the same for my Description tags...
    Cheers, Andrew

  19. Avatar for Mads Cortz
    Mads Cortz December 14th, 2011

    After searching for one hour, i found this article, and i solved it!
    Thx!!

  20. Avatar for Mitzi
    Mitzi September 25th, 2013

    thank you so much!