This guide provides full documentation for the XperienceCommunity.ProjectSettings library.
- Installation
- Creating a Settings Class
- Service Registration
- Creating Admin UI Pages
- Using Settings in Your Code
- Direct Service Access
- Settings Resolution
- Admin Navigation
- Key Types
- Available Form Components
- Generate Code with GitHub Copilot
- Database Structure
- Best Practices
- Troubleshooting
dotnet add package XperienceCommunity.ProjectSettingsCreate a class that implements IProjectSettingsType:
using CMS.ContentEngine;
using XperienceCommunity.ProjectSettings.Classes;
using Kentico.Xperience.Admin.Base.FormAnnotations;
namespace MyProject.Settings;
public class SeoSettings : IProjectSettingsType
{
public string SettingsSlug => "seo-settings";
public string SettingsName => "Seo.Settings";
public string SettingsDisplayName => "SEO Settings";
public string SettingsExplanation => "Configure SEO settings for your website.";
[TextInputComponent(Label = "Meta Title Suffix", Order = 1)]
public virtual string MetaTitleSuffix { get; set; } = "";
[TextAreaComponent(Label = "Default Meta Description", Order = 2)]
public virtual string DefaultMetaDescription { get; set; } = "";
[CheckBoxComponent(Label = "Enable Open Graph", Order = 3)]
public virtual bool EnableOpenGraph { get; set; } = true;
[ContentItemSelectorComponent(
ImageContent.CONTENT_TYPE_NAME,
Label = "Logo",
MinimumItems = 0,
MaximumItems = 1,
Order = 4)]
public virtual IEnumerable<ContentItemReference> LogoImage { get; set; } = [];
}using XperienceCommunity.ProjectSettings;
var builder = WebApplication.CreateBuilder(args);
// Register each settings type you want to use
builder.Services.AddProjectSettings<SeoSettings>();
builder.Services.AddProjectSettings<EmailSettings>();
builder.Services.AddProjectSettings<AnalyticsSettings>();
var app = builder.Build();
app.Run();The AddProjectSettings<T>() method:
- Registers
IOptions<T>for dependency injection - Automatically registers the module installer (creates database table on first run)
- Automatically registers
IProjectSettingsOptionsServicefor direct access - Is idempotent — safe to call multiple times
Inherit from GlobalSettingsEditPage<T>:
using Kentico.Xperience.Admin.Base;
using Kentico.Xperience.Admin.Base.Forms;
using Kentico.Xperience.Admin.Base.Forms.Internal;
using XperienceCommunity.ProjectSettings;
using XperienceCommunity.ProjectSettings.Pages;
using MyProject.Settings;
using MyProject.UISettings;
[assembly: UIPage(
parentType: typeof(ProjectSettingsApplication),
slug: "seo-settings",
uiPageType: typeof(SeoGlobalSettingsPage),
name: "SEO Settings",
templateName: TemplateNames.EDIT,
order: 1)]
namespace MyProject.UISettings;
public class SeoGlobalSettingsPage(
IFormItemCollectionProvider formItemCollectionProvider,
IFormDataBinder formDataBinder)
: GlobalSettingsEditPage<SeoSettings>(formItemCollectionProvider, formDataBinder)
{
}Inherit from ChannelSettingsEditPage<T>:
using CMS.ContentEngine;
using CMS.DataEngine;
using Kentico.Xperience.Admin.Base;
using Kentico.Xperience.Admin.Base.Forms;
using Kentico.Xperience.Admin.Base.Forms.Internal;
using XperienceCommunity.ProjectSettings.Pages;
using MyProject.Settings;
using MyProject.UISettings;
[assembly: UIPage(
parentType: typeof(ProjectSettingsChannelEditSection),
slug: "seo-settings",
uiPageType: typeof(SeoChannelSettingsPage),
name: "SEO Settings",
templateName: TemplateNames.EDIT,
order: 1)]
namespace MyProject.UISettings;
public class SeoChannelSettingsPage(
IFormItemCollectionProvider formItemCollectionProvider,
IFormDataBinder formDataBinder,
IInfoProvider<ChannelInfo> channelInfoProvider)
: ChannelSettingsEditPage<SeoSettings>(formItemCollectionProvider, formDataBinder, channelInfoProvider)
{
}You can use the same settings class for both global and channel-specific admin pages. This enables global defaults with per-channel overrides.
Important: When creating both admin pages for the same settings class, use the same slug value in both UIPage attributes.
public class EmailSettings : IProjectSettingsType
{
public const string PAGE_SLUG = "email-settings";
public string SettingsSlug => PAGE_SLUG;
public string SettingsName => "Email.Settings";
public string SettingsDisplayName => "Email Settings";
public string SettingsExplanation => "Configure email settings.";
[TextInputComponent(Label = "From Email", Order = 1)]
public virtual string FromEmail { get; set; } = "noreply@example.com";
}Create both admin pages using the same slug:
// Global page
[assembly: UIPage(
parentType: typeof(ProjectSettingsApplication),
slug: EmailSettings.PAGE_SLUG,
uiPageType: typeof(EmailGlobalSettingsPage),
name: "Email Settings",
templateName: TemplateNames.EDIT,
order: 1)]
// Channel page
[assembly: UIPage(
parentType: typeof(ProjectSettingsChannelEditSection),
slug: EmailSettings.PAGE_SLUG,
uiPageType: typeof(EmailChannelSettingsPage),
name: "Email Settings",
templateName: TemplateNames.EDIT,
order: 1)]Register once in Program.cs:
builder.Services.AddProjectSettings<EmailSettings>();using Microsoft.Extensions.Options;
public class SeoService
{
private readonly SeoSettings _settings;
public SeoService(IOptions<SeoSettings> settings)
{
_settings = settings.Value;
}
public string GetPageTitle(string baseTitle)
{
return $"{baseTitle} {_settings.MetaTitleSuffix}";
}
}For scenarios where you need more control (e.g., retrieving settings for a specific channel):
using XperienceCommunity.ProjectSettings.Services;
public class MultiChannelService
{
private readonly IProjectSettingsOptionsService _settingsService;
public MultiChannelService(IProjectSettingsOptionsService settingsService)
{
_settingsService = settingsService;
}
public void ProcessChannel(int channelId)
{
// Get settings for a specific channel (with fallback to global)
var settings = _settingsService.GetSettings<SeoSettings>(channelId);
// Get global settings explicitly
var globalSettings = _settingsService.GetSettings<SeoSettings>(0);
}
}When you inject IOptions<T>, settings are resolved automatically based on the current website channel context:
- Channel-specific settings are checked first (based on
IWebsiteChannelContext.WebsiteChannelID) - Global settings (channelId = 0) are used as fallback
- Default values from the class are used if nothing is configured
- Global Settings: Settings → Project Settings → [Your Settings Page]
- Channel Settings: Settings → Project Settings → Channel Settings → [Select Channel] → [Your Settings Page]
Both admin pages base their state purely on whether a settings record exists — not on whether the stored values happen to match the developer defaults. This means the editable form only appears once you have explicitly created an override, and the callouts always describe which values are currently in effect. Creating or resetting an override reloads the page, so the form and callouts update immediately without a manual browser refresh.
Global Settings Page:
- When no global record exists, the form is disabled and previews the developer defaults. A "Using developer defaults" callout with a Create override button lets you store an initial record so the settings become editable.
- When a global record exists, a Reset to developer defaults page action deletes the record (with a destructive confirmation dialog) so the system falls back to the developer defaults.
- Lists the channels that have their own settings (as links). When creating global settings, it notes that those channels already override the global values and will not be affected.
Channel Settings Page:
- When no channel record exists, the form is disabled and previews the effective values — the global settings when they exist, otherwise the developer defaults. A Create override button seeds a new channel record from those effective values, so creating an override does not change the values already in effect.
- When a channel record exists, a Reset page action removes it (with a destructive confirmation dialog). It is labeled Reset to global settings when global settings exist, or Reset to developer defaults when they do not.
- An "Overriding global settings" callout links back to the global settings page when the channel record overrides global values.
- Only supports Website channels.
| Type | Namespace | Description |
|---|---|---|
IProjectSettingsType |
XperienceCommunity.ProjectSettings.Classes |
Interface for settings classes |
GlobalSettingsEditPage<T> |
XperienceCommunity.ProjectSettings.Pages |
Base class for global settings admin pages |
ChannelSettingsEditPage<T> |
XperienceCommunity.ProjectSettings.Pages |
Base class for channel settings admin pages |
ProjectSettingsApplication |
XperienceCommunity.ProjectSettings |
Parent for global settings pages |
ProjectSettingsChannelEditSection |
XperienceCommunity.ProjectSettings.Pages |
Parent for channel settings pages |
IProjectSettingsOptionsService |
XperienceCommunity.ProjectSettings.Services |
Service for direct settings access |
| Attribute | C# Type | Description |
|---|---|---|
[TextInputComponent] |
string |
Single-line text input |
[TextAreaComponent] |
string |
Multi-line text area |
[NumberInputComponent] |
int? |
Integer number input |
[DecimalNumberInputComponent] |
decimal? |
Decimal number input |
[CheckBoxComponent] |
bool |
Boolean checkbox |
[DropDownComponent] |
string |
Dropdown selection (single option) |
[RadioGroupComponent] |
string |
Radio button group (single selection) |
[RichTextEditorComponent] |
string |
Rich text editor (HTML) |
[CodeEditorComponent] |
string |
Code editor with syntax highlighting |
[PasswordComponent] |
string |
Password input (obscured text) |
[DateInputComponent] |
DateTime? |
Date selector |
[DateTimeInputComponent] |
DateTime? |
Date and time selector |
[ExtensionSelectorComponent] |
string |
Allowed file extensions selector |
| Attribute | C# Type | Description |
|---|---|---|
[ContentItemSelectorComponent] |
IEnumerable<ContentItemReference> |
Content item selector (pages, images, reusable content) |
[WebPageSelectorComponent] |
IEnumerable<WebPageRelatedItem> |
Web page selector (pages from website channel content tree) |
[UrlSelectorComponent] |
string |
URL selector (external URL or page from content tree) |
[TagSelectorComponent] |
IEnumerable<TagReference> |
Taxonomy tag selector |
[ObjectSelectorComponent] |
IEnumerable<ObjectRelatedItem> |
Database object selector (users, etc.) |
[GeneralSelectorComponent] |
IEnumerable<string> |
Custom data selector with search |
[EmailSelectorComponent] |
IEnumerable<EmailRelatedItem> |
Email selector (from email channels) |
[FormSelectorComponent] |
IEnumerable<ObjectRelatedItem> |
Form selector |
[SmartFolderSelectorComponent] |
SmartFolderReference |
Smart folder selector |
[ContentFolderSelectorComponent] |
int |
Content folder selector |
Namespace notes:
- Most attributes:
Kentico.Xperience.Admin.Base.FormAnnotationsWebPageSelectorComponent:Kentico.Xperience.Admin.Websites.FormAnnotations(WebPageRelatedItemis inCMS.Websites)EmailSelectorComponent:Kentico.Xperience.Admin.DigitalMarketing.FormAnnotationsTagSelectorComponent/ContentItemSelectorComponent:Kentico.Xperience.Admin.Base.FormAnnotationsorKentico.Xperience.Admin.Content.FormAnnotations
The WebPageSelectorComponent enables users to select pages from a website channel's content tree. It returns IEnumerable<WebPageRelatedItem> objects identified by page GUIDs.
Configuration properties:
- TreePath – limits selection to a subtree (e.g.,
"/Articles") - MaximumPages – max selectable pages (default: 1, use 0 for unlimited)
- Sortable – enables ordering of selected pages
- ItemModifierType – a type implementing
IWebPagePanelItemModifierto disable specific pages from selection
using CMS.Websites;
using Kentico.Xperience.Admin.Websites;
using Kentico.Xperience.Admin.Websites.FormAnnotations;
[WebPageSelectorComponent(
TreePath = "/Articles",
MaximumPages = 5,
// Built-in modifier that prevents selection of folders
// and pages whose content type is not included in routing
ItemModifierType = typeof(WebPagesWithUrlWebPagePanelItemModifier),
Label = "Pages",
Order = 1)]
public virtual IEnumerable<WebPageRelatedItem> Pages { get; set; } = new List<WebPageRelatedItem>();Note: The
WebPageSelectorComponentattribute is in theKentico.Xperience.Admin.Websites.FormAnnotationsnamespace (notKentico.Xperience.Admin.Base.FormAnnotations).
Use the included GitHub Copilot prompt to quickly scaffold new settings. Copy it to your project's .github/prompts/ directory or use it directly in Copilot Chat.
Create project settings using XperienceCommunity.ProjectSettings with:
Settings Name: Newsletter
Properties:
- FromEmail (string, TextInput, default: "newsletter@example.com")
- FromName (string, TextInput, default: "Newsletter")
- EnableDoubleOptIn (bool, CheckBox, default: true)
- WelcomeMessage (string, RichText, default: "")
Target: Both global and channel settings
Settings are stored in the XperienceCommunity_ProjectSettings table (created automatically on first application start):
| Column | Description |
|---|---|
ProjectSettingsChannelID |
0 for global settings, or the website channel ID |
ProjectSettingsName |
Unique identifier (from SettingsName) |
ProjectSettingsValue |
JSON serialized settings object |
ProjectSettingsDisplayName |
Display name in admin UI |
- Naming Convention: Use descriptive, hierarchical names for
SettingsName(e.g.,"Email.Smtp.Settings") - Default Values: Always provide sensible defaults in your settings classes
- SettingsSlug: Must match the
slugin yourUIPageattribute for admin UI links to work - Documentation: Use
SettingsExplanationto provide helpful context for administrators - Virtual Properties: Mark properties as
virtualto support proxy generation if needed - Shared Slugs: When using both global and channel admin pages, use the same
slugvalue
- Ensure your settings class implements
IProjectSettingsType - Verify the admin UI page has the correct
parentTypeand base class - Rebuild and restart the application
- Check that settings have been saved in the admin interface
- Verify
SettingsNamematches between your class and database
- Verify channel-specific settings have been saved
- Check that
IWebsiteChannelContextis resolving the current channel