Parent-Child relationship in Content hub, any experience?

In content strategy choices upgrading from KX13 I often struggle with the choice where to put the data when a true parent/child relation would be desired. In KX13 you would have a tree-node, with child nodes, but moving those to the contenthub would feel like a better place, because of reusability and not the need to have that content within a channel.

Especially in cases where a child would have no meaning on it's own, and you don't want to have ghost-records it would be nice if those child-records would be strictly bound to a parent:

  • Deleting the parent would also delete children

  • Child elements preferably cannot live on their own


Example:
EducationContent (containing info about the education)
EducationStartdateContent (containing info on startdate, and extra info for that date, like location etc.)


I know it is possible to prevent that EducationStartdateContent would be bound more than once, but what if EducationContent is deleted? It does not feel right to come up with a cleanup task, removing the ghost records afterwards, and there is no need to see the EducationStartdateContent in the contenthub itself, but only through it's parent.


Would it be an idea ContentItemSelector forcing parent child relationship, and to hide contentitems in the contenthub for which a parentid is set?

My question to the community: what are your solutions for this? Module classes? Use pages instead of reusable? Or something else?


Environment

  • Xperience by Kentico version: [31.3.3].
Tags:
Content modeling Content hub Content types

Answers

Hi Roel,

Great question and a very common pain point when migrating from KX13 to XbK. You've correctly identified that the Content Hub's reusability-first design conflicts with ownership semantics. Here's how I'd approach it:

The core problem The Content Hub has no native concept of ownership — items can be referenced by many parents, so cascade deletion isn't built in by design. What you need isn't a reference relationship, it's an ownership relationship.

Option 1 — Custom module table (recommended for your use case) Keep EducationContent as a Content Hub item, but store EducationStartdateContent as a custom module class instead of a Content Hub item:

  • Add a ParentContentItemID column (FK to CMS_ContentItem) with ON DELETE CASCADE at the DB level — this gives you automatic cascade delete

  • Build a custom admin UI section scoped to the parent, so child records are never visible as standalone items in the Content Hub

  • Optionally inject the child management UI into the parent's edit form via a custom form component

This satisfies all your requirements: children cannot exist without a parent, deleting the parent cascades automatically, and ghost records are impossible.

Option 2 — Pages (when channel binding is acceptable) If the education content is always tied to one channel and reusability across channels isn't needed, the page tree remains a valid choice. XbK pages still support the parent-child tree structure you know from KX13, with cascade delete built in. You lose Content Hub benefits, but for channel-specific content this is often the cleanest solution with zero custom code.

Option 3 — Soft enforcement via application logic If you want to stay entirely within the Content Hub but avoid the custom module overhead, you can implement a softer ownership pattern in application code:

  • Use a dedicated IsStandalone boolean field on EducationStartdateContent — set to false when created via a parent selector, preventing it from being independently published or selected elsewhere

  • Hook into ContentItemInfo.TYPEINFO.Events.Delete on the parent to explicitly delete all referenced child items in the same transaction

  • Restrict the child content type from appearing in global Content Hub listings via a custom admin UI filter or a scoped ContentItemSelector that only surfaces these items in the context of their parent

This avoids ghost records through convention and code rather than DB constraints. It's more fragile than Option 1 but requires no schema changes outside the Content Hub.

On your ContentItemSelector idea Your instinct is sound. A ParentID-aware selector that hides child-bound items from the Content Hub would be a valuable addition. I'd pair it with an event handler on ContentItemInfo.TYPEINFO.Events.Delete as a safety net for reference-based relationships where you don't control DB-level cascades.

Bottom line: For strict ownership with no independent child meaning, go with Option 1 (custom module). Option 2 is the zero-code path if channel binding is acceptable. Option 3 is a middle ground if you need to stay within Content Hub conventions but can accept application-level enforcement over database-level guarantees.

@Pawan, please don't post AI given answers, I'm sure if they wanted AI they would ask for it... I wouldn't recommend really any of those options.

To give a BETTER answer, I may need a little more context though. If the child can't exist without the parent, these questions should be asked:

  1. Should it exist then as a separate Content Item, vs. more fields on the parent?

    1. If possible of multiple instances of course then the answer would be to keep it as separate content, OR as a structured JSON field (more on that in a bit)
    2. If not, then may make sense to just add it as a field.
  2. Is there any sharing or 'savings' in sharing?

Firstly, I wouldn't worry too much about having items in the content hub, it can handle it (trust me), so you won't experience any performance issues. However, I totally get wanting to keep the content hub clean. Here's some REAL options:

Use a JSON structured content Field : In Xperience, you can create your own data types and then instruct the system in parsing the model in C# into things like a List of items. This can allow you to store "many" relationships on the parent without creating sub children. https://docs.kentico.com/documentation/developers-and-admins/customization/field-editor/data-type-management#register-custom-data-types.

Downside to this though is often you have to build a custom editor for it.

Create a Cleanup Script : If the children are only used by the parents, you could do a cleanup script to get ALL the parents (including those not published or in edit mode), loop through all the children and get the content item guids, then do a query to get all the children and find all the content item guids that are NOT in the parent list. Delete the ones not in the parent list.

Hook into the Parent Delete to Delete Children There are multiple places you can hook into the delete event and then delete the children, there are some risks with this (like if they accidently delete it, and then go to restore it, the children may also be deleted too). https://docs.kentico.com/documentation/developers-and-admins/customization/handle-global-events/handle-content-events#content-item-events

Hope this helps!

Thanks Trevor,

Our real-world example for this is a synchronisation. ContentItems are created in a sched. task, and the parent items are reusable, but containing not-reusable contents.

So it the structured solution would be an idea, using json. Creating an editor for it might not be that much of a deal, we've done that already and have a generic solution for it, binding a class to an edit-UI. (see the project settings we talked about).

But we do also have a child within that child, making it even more complex ...

The more I think of it, the more I tend to moduleclasses, giving the complete flexibility. You would miss the Used-in functionality though...

For now I guess the Child delete hookup and clean script would be the quickest option, and extend the child names with it's parent name. Adding it's external ID's as a field might make that a lot easier.

To response this discussion, you have to login first.