Sitecore has a pretty cool stuff called Bucketing which lets you store and manage huge amount of content items in one single container. I guess you already know what I’m talking about so I will skip explaining how to work with bucket items.
This post will explain how an orphan bucket folder will be removed dynamically removed when a bucketable item is deleted from the Sitecore tree using a custom event handler.
First, we will need to define our new custom event who should be placed into the item:deleted. This is the event that is raised when an item has been deleted. The subscription of the event is in the following way:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<events>
<event name="item:deleted">
<handler type="Project.Website.Configuration.CleanupBucket, Project.Website" method="OnItemDeleted" />
</event>
</events>
</sitecore>
</configuration>
Then we have to implement a class that will loop thought all the ancestors of the deleted item and will remove the bucket folders that are not having a bcuketable child item.
namespace Project.Website.Configuration
{
public class CleanupBucket
{
/// <summary>
/// Called when [item deleted complete].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="eventArgs">The <see cref="EventArgs"/> instance containing the event data.</param>
protected virtual void OnItemDeleted(object sender, EventArgs eventArgs)
{
var sitecoreArgs = eventArgs as Sitecore.Events.SitecoreEventArgs;
if (sitecoreArgs == null) return;
var item = Sitecore.Events.Event.ExtractParameter(eventArgs, 0) as Item;
if (item == null || item.IsABucket() || !item.IsItemBucketable()) return;
var parentId = Sitecore.Events.Event.ExtractParameter(eventArgs, 1) != null ? Sitecore.Events.Event.ExtractParameter(eventArgs, 1).ToString() : null;
var parentItem = !string.IsNullOrWhiteSpace(parentId) ? item.Database.GetItem(new ID(parentId)) : null;
CleanupOrphanBucket(item, parentItem);
}
protected void CleanupOrphanBucket(Item item, Item currentParentItem = null)
{
Assert.IsNotNull(item, "item");
var parentItem = currentParentItem ?? item.Parent;
if (parentItem == null || !parentItem.IsABucketFolder() || parentItem.Axes.GetDescendants().Any(child => !child.IsABucketFolder())) return;
using (new Sitecore.SecurityModel.SecurityDisabler())
{
var parentAncestor = parentItem.Parent;
parentItem.Delete();
CleanupOrphanBucket(parentItem, parentAncestor);
}
}
}
}
Finally, verify that your merged configuration has your class into the <event> configuration node. Consider including this event also for the remove event handler (item:deleted:remote)

For more information, you can visit Sitecore Buckets.
Happy Sitecoring 😉