When you move from Sitecore to Contentful, the content itself almost always survives the trip. The words, the images, the fields: they land. What breaks is the relationships between them, because Sitecore knits its content together with GUIDs stored in a tree, and Contentful knits its content together with entry references in a flat space. Migrate the pages and forget the wiring, and you get a site full of articles that no longer know which author wrote them, which product they belong to, or where their hero image went.
Imagine you spend a weekend moving your entire office into a new building. Every desk arrives. Every chair arrives. Then Monday comes, and you discover that the internal phone directory still lists everyone at their old extensions. You call "Dave in Legal, extension 42" and reach a filing cabinet. Nobody's gone. Everybody's just unreachable. That is exactly what an unmapped Sitecore-to-Contentful migration feels like.
Why the relationships are the hard part
Sitecore stores everything as items in a content tree, and it links items together using their GUID. A Droplink field, a Multilist, a Treelist: under the hood, those are all just lists of GUIDs pointing at other items. Rich text is worse, because Sitecore rewrites internal links into placeholders like <a href="~/link.aspx?_id={22222222-...}"> and media into -/media/{GUID}.ashx. The GUID is the relationship. It is the extension number in the directory.
Contentful also links by ID. Every entry and asset has a sys.id, and reference fields store a little Link object that points at it. The problem is that Contentful assigns brand new IDs on import. The moment your content lands, every GUID in your old data is a phone number that now rings a stranger. If you do not build a lookup table from old GUID to new sys.id before you resolve references, you are not migrating relationships. You are severing them and hoping nobody calls.
The field types that lie to you
Here is the trap that eats a week of your schedule. Most Sitecore reference fields store GUIDs, so you can find and remap them. But not all of them. A Droplist stores the display value as plain text, not a GUID. A Name Value List stores text too. So your export script, quite reasonably, tries to resolve every reference field as a GUID, and these two return garbage or nothing at all. You will not notice until an editor asks why the "Region" dropdown migrated as an empty string.
| Sitecore field | What it actually stores | Maps to in Contentful |
|---|---|---|
| Droplink, Droptree | Single GUID | Reference (one entry) |
| Multilist, Treelist, Checklist | Pipe-separated GUIDs | Reference (many entries) |
| Grouped Droplink | Single GUID | Reference (one entry) |
| Droplist | Plain text value | Short text or a lookup entry |
| Name Value List | Query-string text | JSON object field |
| Rich Text internal links | GUID inside href | Rich Text embedded/inline entry links |
Sort every field into that table before you write a single line of import code. The ones that store text need a different path than the ones that store GUIDs, and mixing them up is the single most common reason a Sitecore export comes out looking complete and arrives broken.
Decoupling the front end means throwing away the presentation
Sitecore keeps layout inside the content. Presentation details, renderings, datasource items, the whole rendering tree: it all lives on the item. Contentful has none of that, and that is the point. Your new front end, whatever you build it in, owns presentation now. So the migration is not a copy. It is a filter. You keep the structured content and the references, and you deliberately drop the layout metadata, because a React or Next front end does not want to be told which placeholder a component sat in on a 2016 Sitecore instance.
The gotcha hiding here: Sitecore's shared components are backed by datasource items, and those datasources are real content with real references. Kill the presentation layer carelessly and you orphan the content those components pointed at. Walk the datasources first, pull their content into proper Contentful entries, then let the front end decide how to show them.
The limits that bite at import time
Two numbers worth knowing before you start. Contentful's REST API resolves links up to an include depth of 10 levels, so a chain of references deeper than that will not fully hydrate in one call. And a single entry has a size ceiling, so if you were leaning on one enormous Sitecore item with hundreds of Multilist references, you may need to reshape rather than copy. Deeply nested Sitecore trees that relied on inheritance and hierarchy do not have a native home in Contentful's flat model, so hierarchy usually becomes an explicit parent reference field you design on purpose.
What I'd actually do
Build the old-GUID-to-new-ID map first, treat it as the crown jewels, and do not resolve a single reference until it exists. Import in two passes: create every entry and asset first so they all have IDs, then go back and wire the references and rewrite the rich-text links against the map. Audit the Droplist and Name Value List fields by hand because your script will quietly get them wrong. And crawl the old site before you touch anything, so you know exactly which media GUIDs and internal links are load-bearing and which are pointing at things that died three redesigns ago. That inventory is the part people skip and the part that costs them, and it's the job we built the Sitecore-to-Contentful guide and the crawler around.
The content will make it across. It always does. Your only real job is making sure that when the new site calls Dave in Legal, it isn't a filing cabinet that answers.
