Home Services Work About Blog Contact Let's Talk
Home/ Blog/ SharePoint Content Types & Site Columns
SharePoint Online

SharePoint Content Types & Site Columns: Enterprise Governance Done Right

Why Content Types Are the Foundation of SharePoint Governance

Every SharePoint Online project we've worked on over the past decade has confirmed the same fundamental truth: organizations that invest in content types early save enormous time and pain later. Content types are the mechanism that lets you define what a piece of content is — not just where it lives. A contract, an invoice, a policy document, a project brief — each has its own identity, its own set of required metadata, its own lifecycle. Without content types, these distinctions exist only in your users' heads, and SharePoint becomes an expensive network drive.

The SharePoint Catalog Management public preview that rolled out in October–November 2025 made structured metadata governance more visible than ever. Organizations are now realizing that good governance isn't a set of rules bolted on after deployment — it's an architectural decision made before the first library is created. Content types are the cornerstone of that architecture. They control what columns appear on a document, what values are required before saving, what retention policy applies, and even what workflow is triggered when a document reaches a certain status.

In practice, we define content types at three levels in every enterprise SharePoint Online deployment. At the tenant level, we use the Content Type Hub to define core types that apply across all site collections — contract, financial report, policy document. At the site collection level, we extend those core types with department-specific columns — a legal department needs jurisdiction and counterparty columns that the finance department does not. At the library level, we apply and configure the inherited content types, setting defaults appropriate for that specific library's context. This three-tier approach keeps governance centralized while allowing legitimate local variation.

Content types also form the backbone of Microsoft 365 Copilot's ability to understand your content. When Copilot AI tries to answer a question about contracts, it uses the content type metadata to identify which documents are contracts, who owns them, and what their current status is. Without that semantic layer, Copilot has to guess — and it will guess wrong. Building content types correctly today is directly investing in the quality of your AI-assisted work tomorrow.

Site Columns vs. List Columns: Always Choose Site Columns

This is the single most common governance mistake we see on SharePoint Online engagements: teams create columns directly on individual lists and libraries rather than defining them as site columns first. The immediate consequence is that the same semantic concept — say, "Contract Status" — ends up defined in twenty different ways across twenty different libraries. Some use choice columns, some use text columns, some spell it "ContractStatus" and others "Contract_Status". Reporting becomes impossible and Copilot AI produces inconsistent results.

Site columns are column definitions stored at the site or site collection level rather than attached to a specific list. When you define "Contract Status" as a site column with a specific set of choices, every library that uses that column shares the same definition. If you need to add a new choice value, you change it in one place and it propagates everywhere. This is the difference between a governed environment and chaos. Our rule on every project is absolute: never create a column directly on a list or library if that concept exists or might exist elsewhere in the site collection.

From a PnPjs or SPFx development perspective, site columns are also far easier to work with programmatically. They have stable internal names that don't vary between sites, and you can reference them by their GUID when you need absolute certainty. When building SPFx web parts that display or edit document metadata, referencing site column internal names means your code works across any library that uses the same content type — you don't need to write site-specific adapters. This portability is invaluable in enterprise environments where the same web part might need to render in fifty different department sites.

There's also a performance argument for site columns. SharePoint Online caches site column definitions at the site collection level, meaning that the schema lookup for a library using site columns is faster than for a library using custom list columns. At enterprise scale — hundreds of libraries, thousands of daily users — this matters. We've measured the difference in our load testing and consistently see faster list view rendering when libraries are built on proper site column foundations.

The Content Type Hub: Centrally Managing Types Across Your Tenant

The Content Type Hub is a special site collection in SharePoint Online — typically provisioned at /sites/ContentTypeHub — that acts as the authoritative source for content types published across your entire tenant. Any content type you define and publish from the hub becomes available in every other site collection. This is the mechanism that makes tenant-wide governance possible. Without the hub, content types are scoped to individual site collections and you're back to defining "Invoice" twenty different ways.

Setting up the Content Type Hub correctly requires SharePoint admin access and a deliberate organizational strategy. We start every engagement with a content inventory workshop — identifying all the document types that exist across the organization, grouping them by similarity, and designing a content type hierarchy. SharePoint content types support inheritance, so you might have a base "Akshara Document" type with company-wide columns like "Department", "Document Owner", and "Review Date", from which more specific types inherit and add their own columns. This hierarchy is designed in a spreadsheet before a single content type is created in the hub.

Publishing content types from the hub is straightforward but has one significant operational consideration: changes to hub content types propagate asynchronously via a timer job. In SharePoint Online, this propagation typically completes within 24 hours, but during business-critical migrations we've seen it take longer. This means you cannot make a breaking change to a hub content type and expect it to be live in all site collections by the afternoon. Plan your rollout schedule accordingly, and always test changes in a non-production tenant first.

PowerShell — Push Content Type Hub Updates
# Connect to the Content Type Hub
Connect-PnPOnline -Url "https://tenant.sharepoint.com/sites/ContentTypeHub" `
    -Interactive

# Get a specific content type
$ct = Get-PnPContentType -Identity "Akshara Contract"

# Publish the content type to subscriber sites
Set-PnPContentType -Identity $ct.Id `
    -UpdateChildren $true

# Force immediate push (triggers the timer job)
$hubUrl = "https://tenant.sharepoint.com/sites/ContentTypeHub"
$ctx = Get-PnPContext
$subscriberUrls = Get-PnPContentTypePublishingHubUrl
Write-Host "Hub URL: $subscriberUrls"

# Check sync status across subscriber sites
Get-PnPTenantSite -Detailed | ForEach-Object {
    Write-Host "Checking: $($_.Url)"
}

Designing Your Taxonomy: Column Names, Types, and Defaults

The quality of your content type taxonomy directly determines the quality of your governance outcomes. A well-designed taxonomy makes SharePoint search powerful, Copilot AI accurate, and reporting straightforward. A poorly designed taxonomy is worse than no taxonomy at all — it creates false confidence while delivering none of the benefits. We follow several principles that have proven themselves across dozens of enterprise SharePoint Online deployments.

Column naming is more important than most teams realize. Internal names in SharePoint are set at creation and cannot be changed — only the display name can be modified afterward. If you create a column with the internal name "ContractStatus_x" because you made a typo in the display name and then changed it, that internal name follows that column forever. Every PnPjs query, every CAML query, every Power Automate flow that references that column by its internal name will need updating if you try to "fix" it by recreating the column. Our practice is to define a naming convention document — PascalCase for internal names, human-readable for display names — and enforce it as part of the content type review process before anything is created in the hub.

Column type selection requires careful thought about future querying patterns. Managed Metadata columns (term store) are ideal for controlled vocabularies that might grow or change — department names, project categories, regulatory frameworks. Choice columns are appropriate for small, stable sets of values — document status (Draft, Under Review, Approved, Archived). People or Group columns should be used for ownership rather than free-text fields, because they enable permission-based queries and integrate with the Microsoft 365 profile system. Date columns for review and expiry dates are essential for any retention policy implementation. Number and currency columns need explicit precision and locale settings defined at creation time.

XML — Site Column Definition (Elements.xml)
<!-- Site column definitions for deployment via PnP Provisioning -->
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <Field ID="{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
         Name="AksharaDocumentStatus"
         DisplayName="Document Status"
         Type="Choice"
         Required="FALSE"
         Group="Akshara Columns">
    <CHOICES>
      <CHOICE>Draft</CHOICE>
      <CHOICE>Under Review</CHOICE>
      <CHOICE>Approved</CHOICE>
      <CHOICE>Archived</CHOICE>
    </CHOICES>
    <Default>Draft</Default>
  </Field>

  <Field ID="{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"
         Name="AksharaReviewDate"
         DisplayName="Review Date"
         Type="DateTime"
         Format="DateOnly"
         Required="FALSE"
         Group="Akshara Columns">
  </Field>

  <Field ID="{C3D4E5F6-A7B8-9012-CDEF-123456789012}"
         Name="AksharaDocumentOwner"
         DisplayName="Document Owner"
         Type="User"
         Required="FALSE"
         Group="Akshara Columns"/>

</Elements>

Document Sets: Grouping Related Files Under One Content Type

Document Sets are one of SharePoint's most underused governance features. A Document Set is a special content type that acts as a folder with metadata — it groups related documents together and treats them as a single unit for versioning, workflow, and retention purposes. Consider a contract negotiation process: you have the initial proposal, several versions of the redlined contract, supporting exhibits, and the final executed agreement. Without Document Sets, these live in the same library as an ungrouped mess of files. With Document Sets, they live together in a single container that has its own content type, its own shared metadata (counterparty name, contract value, execution date), and its own version history.

In our enterprise intranet deployments, Document Sets shine in project management libraries, HR case management, and legal matter management scenarios. A Project Document Set contains the project charter, weekly status reports, meeting minutes, and deliverables — and the set itself carries the project metadata (project code, client name, project manager, start date, completion date). When you search for all documents related to Project Alpha, you get the set and can navigate directly to the right folder. When the project closes, a single retention policy applied to the Document Set disposes of all documents within it according to the schedule.

Enabling Document Sets requires activating the "Document Sets" site collection feature, which is done via SharePoint admin center or PnP PowerShell. Once active, Document Sets appear as a content type that inherits from Folder, and you can configure which content types are allowed within the set, which columns are shared from the set to its documents, and what the default welcome page looks like. The welcome page is an underrated feature — it can be a web part page that shows a filtered view of documents in the set, key metadata, and recent activity.

REST API — Create a Document Set Instance
// POST to create a Document Set via SharePoint REST API
const createDocumentSet = async (
  siteUrl: string,
  libraryTitle: string,
  setName: string,
  metadata: Record<string, unknown>
) => {
  const headers = {
    'Accept': 'application/json;odata=verbose',
    'Content-Type': 'application/json;odata=verbose',
    'X-RequestDigest': await getRequestDigest(siteUrl)
  };

  // Step 1: Create the Document Set folder
  const body = {
    __metadata: { type: 'SP.Folder' },
    ServerRelativeUrl: `/sites/mysite/${libraryTitle}/${setName}`
  };

  const folderResponse = await fetch(
    `${siteUrl}/_api/web/folders`,
    { method: 'POST', headers, body: JSON.stringify(body) }
  );

  const folder = await folderResponse.json();
  const listItemId = folder.d.Properties.__metadata.id;

  // Step 2: Set the content type to Document Set
  await fetch(
    `${siteUrl}/_api/web/lists/getbytitle('${libraryTitle}')/items(${listItemId})`,
    {
      method: 'POST',
      headers: { ...headers, 'X-HTTP-Method': 'MERGE' },
      body: JSON.stringify({
        __metadata: { type: 'SP.Data.ContractsListItem' },
        ContentTypeId: '0x0120D520', // Document Set content type ID
        ...metadata
      })
    }
  );
};

Enforcing Content Types: Mandatory Metadata and Form Customization

Defining content types is only half the work — enforcing them is where governance either succeeds or fails. Without enforcement, users will ignore content types, skip required fields, and revert to dumping files without metadata. Enforcement has to be thoughtful, though: if the governance burden is too high, users route around it by uploading documents to Teams channels or personal OneDrive instead. The goal is to make the right thing easy and the wrong thing difficult, not impossible.

Required columns are the first enforcement mechanism. When a column is marked Required on a content type, users must provide a value before the document can be saved. The challenge is knowing which columns to make required versus recommended. In our experience, two or three required fields is the maximum users will tolerate without significant friction — Department, Document Type, and Document Status are typically the right choices. Everything else should be highly encouraged via default values, conditional formatting, and form customization rather than hard requirements.

SharePoint Online's column formatting and form customization features (introduced via JSON-based column formatting) allow you to build rich, guided metadata capture experiences without writing any SPFx code. You can show help text next to a column, conditionally hide columns based on content type selection, and highlight missing fields with visual indicators. For more complex scenarios, we build custom SPFx form customizers — extensions that replace or augment the standard list form with a tailored interface that walks users through metadata capture step by step, validates fields against business rules, and pre-populates values from external systems.

Retention and Information Management Policies on Content Types

Content types are the natural attachment point for retention policies in SharePoint Online. Microsoft 365 compliance center allows you to publish retention policies that target content types — any document that matches that content type, regardless of which library it lives in, will be subject to the policy. This is dramatically more powerful than applying retention to individual libraries, because your content governance follows the content rather than the container. A "Financial Report" content type carries a 7-year retention requirement everywhere it goes, even if it's created in a new library next year that didn't exist when the policy was written.

Implementing retention on content types requires coordination between SharePoint governance and your compliance/legal teams. The compliance center's retention policy wizard will ask you to specify whether you want to retain content, delete it, or retain and then delete. For most enterprise scenarios, the pattern is retain-then-delete: keep documents for the required period and then automatically move them to the recycle bin or, for records, prevent any modification during the retention period. Content types can be marked as Records in SharePoint, which means once a document is declared a record (either manually or via an automated retention label trigger), it cannot be edited or deleted until the retention period expires.

We've implemented information management policies — the older, library-level predecessor to Microsoft 365 retention labels — on content types as well, and they remain useful for scenarios like document expiry notifications. When a "Policy Document" content type has a six-month review cycle, an information management policy can automatically send an email to the Document Owner column recipient when the ReviewDate column value is within 30 days. This kind of automated governance reminder, triggered by content type metadata, keeps document libraries accurate without requiring a human to run monthly reports.

Working with Content Types Programmatically via PnPjs

When building SPFx web parts and extensions that interact with content-typed documents, PnPjs is the library we reach for every time. Its fluent API makes content type operations readable and maintainable, and its TypeScript type definitions catch errors at compile time rather than runtime. Working with content types programmatically is essential for any solution that provisions libraries, creates templated content, or builds custom list forms — which covers the majority of enterprise SPFx development.

Creating content types and site columns via PnPjs in a provisioning script ensures repeatable, version-controlled deployments. Rather than clicking through the SharePoint admin center — which is slow, error-prone, and produces no audit trail — we define all content types in TypeScript provisioning scripts that live in our project repository. When a new site collection needs provisioning, we run the script and the environment is built identically every time. This approach is particularly valuable in multi-region enterprises where dozens of site collections need the same content type configuration.

TypeScript — PnPjs Content Type Creation
import { spfi, SPFx } from "@pnp/sp";
import { IContentTypeInfo } from "@pnp/sp/content-types";
import "@pnp/sp/webs";
import "@pnp/sp/content-types";
import "@pnp/sp/fields";
import "@pnp/sp/lists";

const sp = spfi().using(SPFx(context));

// Create a site column first
const ensureSiteColumn = async () => {
  const fieldXml = `<Field
    ID="{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
    Name="AksharaDocumentStatus"
    DisplayName="Document Status"
    Type="Choice"
    Group="Akshara Columns">
    <CHOICES>
      <CHOICE>Draft</CHOICE>
      <CHOICE>Under Review</CHOICE>
      <CHOICE>Approved</CHOICE>
      <CHOICE>Archived</CHOICE>
    </CHOICES>
    <Default>Draft</Default>
  </Field>`;

  const field = await sp.web.fields.createFieldAsXml(fieldXml);
  console.log(`Field created: ${field.Id}`);
  return field;
};

// Create the content type and add the site column to it
const createContractContentType = async () => {
  // Create the content type (inheriting from Document: 0x0101)
  const ct = await sp.web.contentTypes.add(
    "0x010100AE123456789ABCDEF", // custom ID inheriting from Document
    "Akshara Contract",
    "Standard contract document with governance metadata",
    "Akshara Content Types"
  );

  // Add site column to content type
  await sp.web.contentTypes
    .getById(ct.Id.StringValue)
    .fields
    .addField(sp.web.fields.getByInternalNameOrTitle("AksharaDocumentStatus"));

  // Add the content type to a library
  await sp.web.lists
    .getByTitle("Contracts")
    .contentTypes
    .addAvailableContentType(ct.Id.StringValue);

  console.log("Content type created and added to Contracts library");
};

One important PnPjs consideration when working with content types is the order of operations. Site columns must exist before content types that reference them, and content types must be added to the site before they can be added to libraries within that site. When writing provisioning scripts, always verify existence before creation — PnPjs's ensure pattern (e.g., ensureField) handles this gracefully. We also recommend wrapping the entire provisioning sequence in a try-catch with detailed logging so that partial failures can be diagnosed and re-run from the point of failure rather than starting over from scratch.

Key Takeaways

Always use site columns instead of list columns — they're reusable, consistent, and enable tenant-wide governance.

The Content Type Hub enables one-to-many publishing of content types across your entire SharePoint Online tenant.

Document Sets group related files under a single container with shared metadata and unified retention policies.

PnPjs enables version-controlled, repeatable content type provisioning that scales across hundreds of site collections.

Retention policies attached to content types follow the content everywhere it goes, not just the library where it was created.

A

Akshara Technologies

SharePoint Online Specialists

We design and build enterprise SharePoint Online environments — modern intranet portals, content governance frameworks, SPFx solutions, and Power Automate workflows — for organizations across India, USA, UAE, and Australia. SharePoint content type architecture is one of our core specializations.

Need a Governance Framework for Your SharePoint Tenant?

We design content type taxonomies, site column strategies, and hub architectures that scale with your organization. Let's build something that lasts.

Start the Conversation Our SharePoint Services

Related Articles