Updating Navigation programmatically for SharePoint sites is challenging. This has become even more challenging with Modern SharePoint Team sites.

Some of the constraints are below.

  1. All Navigation headers need to have a Url associated to them, they cannot be just placeholders
  2. External Site Urls and Internal Site Urls are identified explicitly in the Navigation and it is not possible to change them without deleting and recreating
  3. Urls cannot be updated on Navigation nodes which already have Urls associated to them. They will need to be re-created
  4. All provided URLs needs to link to valid page in the site for Internal Links
  5. Navigation can be nested three levels deep
  6. Deletion of Navigation nodes needs extra steps and cannot delete

In this blog, we will look at steps on how we can update the Navigation using SharePoint CSOM. Few of the below steps are not possible through PnP PowerShell so CSOM is the best bet on this.

Traversing Navigation nodes

Traverse the navigation items using nested foreach statements to find the exact node to do the changes.


using (var context = new ClientContext(<siteURL>))
{
contextAsset.Credentials = new SharePointOnlineCredentials(UserName, SecurePass);
Web web = contextAsset.Web;
contextAsset.Load(web, w => w.Navigation);
contextAsset.Load(web);
contextAsset.ExecuteQuery();
//Start working on Navigation
NavigationNodeCollection lefthandNav = web.Navigation.QuickLaunch;
contextAsset.Load(lefthandNav);
List<int> navIds = new List<int>(); // To be used in Deletion
contextAsset.ExecuteQuery();
foreach (NavigationNode nodeLeftHandNav in lefthandNav)
{
contextAsset.Load(nodeLeftHandNav.Children);
contextAsset.ExecuteQuery();
if (nodeLeftHandNav.Children.Count > 0)
{
foreach (NavigationNode nodeLeftHandNavChild1 in nodeLeftHandNav.Children)
{
contextAsset.Load(nodeLeftHandNavChild1.Children);
contextAsset.ExecuteQuery();
if (nodeLeftHandNavChild1.Children.Count > 0)
{
foreach (NavigationNode nodeLefthandNavChild2 in nodeLeftHandNavChild1.Children)
{
<Do something>
//For deletion
if(nodeLefthandNavChild2 == TobeDeleted)
navIds.Add(nodeLeftHandNav.Id);
}
}
else
{
<Do something>
//For deletion
if(nodeLeftHandNavChild1 == TobeDeleted)
navIds.Add(nodeLeftHandNav.Id);
}
}
}
else
{
<Do something>
//For deletion
if(nodeLeftHandNavChild1 == TobeDeleted)
navIds.Add(nodeLeftHandNav.Id);
}
}
}

Update URL of Navigation node

Create a new Navigation node using NavigationNodeCreationInformation class with new URL. Add the navigation node after the parent node. Delete the existing node.


using (var contextNavigation = new ClientContext(<siteURL>))
{
contextNavigation.Credentials = new SharePointOnlineCredentials(UserName, SecurePass);
Web web = contextNavigation.Web;
contextNavigation.Load(web, w => w.Navigation);
contextNavigation.Load(web);
contextNavigation.ExecuteQuery();
//Start working on Navigation
NavigationNodeCollection lefthandNav = web.Navigation.QuickLaunch;
contextNavigation.Load(lefthandNav);
contextNavigation.ExecuteQuery();
NavigationNodeCreationInformation nodeToCreate = new NavigationNodeCreationInformation();
NavigationNode navNode = lefthandNav[<num>];
contextNavigation.Load(navNode);
contextNavigation.ExecuteQuery();
if (navNode.Title.ToLower().Contains(<value to check>))
{
nodeToCreate.PreviousNode = navNode;
nodeToCreate.Title = navNode.Title;
nodeToCreate.Url = <NewUrl>;
nodeToCreate.IsExternal = <bool>;
navIds.Add(navNode.Id); //For deleting the existing node after creating the new one
}
if (nodeToCreate.Title != "")
{
lefthandNav.Add(nodeToCreate);
}
//Use gist – https://gist.github.com/AsishP/70511f5eac4f069d8195ed36301758ab to delete
contextNavigation.ExecuteQuery();
}

Delete an Existing node

Note: Deletion cannot be done while traversing the nested Navigation node Collection.

Get the Navigation ID using the below code, put into an Array for deletion and then delete it using Web Object


//Check the gist code – https://gist.github.com/AsishP/b817455ce58b753e4f02ae5a0482e7a4 for filling the NavIds Array
foreach (int id in navIds)
{
NavigationNode nodeToDelete = web.Navigation.GetNodeById(id);
contextAsset.Load(nodeToDelete);
contextAsset.ExecuteQuery();
nodeToDelete.DeleteObject();
contextAsset.ExecuteQuery();
}

Set Navigation Header 

This can be set expliclity through PnPPowerShell using below. For CSOM add it to the top level of the Node collection

Add-PnPNavigationNode -Location QuickLaunch -Title "Test Nav" -Header $true

Conclusion

Above we have got code samples of how we can update Navigation in Modern Team sites using SharePoint CSOM

8 Comments

  1. Hi Asish,
    I want to create custom Left navigation from list(lookup column) in modern site. can you help me
    Like,

    India
    Chennai
    Bangalore
    USA
    California
    Europe
    Germany
    Berlin

    Like

  2. I want to create left navigation from list (lookup column) in sharepoint modern template.. please help to implement.
    India
    -Chennai
    -Bangalore

    India is a header and chennai and bangalore is subheader…

    Like

Leave a comment