SharePoint Framework (SPFx) 1.10 represents a significant milestone in Microsoft’s journey to modernize SharePoint development. Released in early 2020, this version introduces Library Components, enhanced Microsoft Teams integration, and improved developer tooling that fundamentally change how enterprise developers build solutions for the Microsoft 365 ecosystem. In this comprehensive guide, we’ll explore every major feature, provide production-ready code examples, and share best practices accumulated from real-world enterprise deployments.
Understanding Library Components: The Game Changer
Before SPFx 1.10, sharing code between web parts required either npm packages (complex versioning) or duplicating code (maintenance nightmare). Library Components solve this elegantly by allowing you to create reusable code libraries that are deployed once and referenced by multiple web parts at runtime.
How Library Components Work
A Library Component is packaged as a separate .sppkg file but doesn’t render any UI. Instead, it exports JavaScript modules that other SPFx components can consume. The SharePoint framework handles the dependency resolution at runtime, ensuring that duplicate code isn’t downloaded multiple times.
graph TB
subgraph TenantAppCatalog ["Tenant App Catalog"]
Lib[Library Component]
WP1[Web Part A]
WP2[Web Part B]
end
subgraph Runtime ["Browser Runtime"]
LibLoaded[Shared Library Code]
WP1Instance[Web Part A Instance]
WP2Instance[Web Part B Instance]
end
Lib --> LibLoaded
WP1 --> WP1Instance
WP2 --> WP2Instance
WP1Instance --> LibLoaded
WP2Instance --> LibLoaded
style Lib fill:#E1F5FE,stroke:#0277BD
style LibLoaded fill:#C8E6C9,stroke:#2E7D32
Creating Your First Library Component
Let’s build a utility library that provides common data access patterns used across multiple web parts.
# Create the library project
yo @microsoft/sharepoint
# Select the following options:
# - What is your solution name? corporate-utilities
# - Which type of client-side component to create? Library
# - What is your Library name? CorporateUtilities
// src/libraries/corporateUtilities/CorporateUtilitiesLibrary.ts
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
export interface IListItem {
Id: number;
Title: string;
Created: Date;
Modified: Date;
}
export interface IDataService {
getListItems(listTitle: string): Promise<IListItem[]>;
createListItem(listTitle: string, item: Partial<IListItem>): Promise<IListItem>;
updateListItem(listTitle: string, itemId: number, updates: Partial<IListItem>): Promise<void>;
deleteListItem(listTitle: string, itemId: number): Promise<void>;
}
export class DataService implements IDataService {
private spHttpClient: SPHttpClient;
private siteUrl: string;
constructor(spHttpClient: SPHttpClient, siteUrl: string) {
this.spHttpClient = spHttpClient;
this.siteUrl = siteUrl;
}
public async getListItems(listTitle: string): Promise<IListItem[]> {
const endpoint = `${this.siteUrl}/_api/web/lists/getbytitle('${listTitle}')/items?$select=Id,Title,Created,Modified&$orderby=Modified desc&$top=100`;
const response: SPHttpClientResponse = await this.spHttpClient.get(
endpoint,
SPHttpClient.configurations.v1
);
if (!response.ok) {
throw new Error(`Failed to fetch items: ${response.statusText}`);
}
const data = await response.json();
return data.value.map((item: any) => ({
Id: item.Id,
Title: item.Title,
Created: new Date(item.Created),
Modified: new Date(item.Modified)
}));
}
public async createListItem(listTitle: string, item: Partial<IListItem>): Promise<IListItem> {
const endpoint = `${this.siteUrl}/_api/web/lists/getbytitle('${listTitle}')/items`;
const response = await this.spHttpClient.post(
endpoint,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'Content-type': 'application/json;odata=nometadata',
'odata-version': ''
},
body: JSON.stringify(item)
}
);
if (!response.ok) {
throw new Error(`Failed to create item: ${response.statusText}`);
}
return response.json();
}
public async updateListItem(listTitle: string, itemId: number, updates: Partial<IListItem>): Promise<void> {
const endpoint = `${this.siteUrl}/_api/web/lists/getbytitle('${listTitle}')/items(${itemId})`;
const response = await this.spHttpClient.post(
endpoint,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'Content-type': 'application/json;odata=nometadata',
'odata-version': '',
'IF-MATCH': '*',
'X-HTTP-Method': 'MERGE'
},
body: JSON.stringify(updates)
}
);
if (!response.ok) {
throw new Error(`Failed to update item: ${response.statusText}`);
}
}
public async deleteListItem(listTitle: string, itemId: number): Promise<void> {
const endpoint = `${this.siteUrl}/_api/web/lists/getbytitle('${listTitle}')/items(${itemId})`;
const response = await this.spHttpClient.post(
endpoint,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'IF-MATCH': '*',
'X-HTTP-Method': 'DELETE'
}
}
);
if (!response.ok) {
throw new Error(`Failed to delete item: ${response.statusText}`);
}
}
}
Consuming the Library in a Web Part
Once the library is deployed, consuming it in a web part is straightforward. You reference it in the project configuration and import as usual.
// config/config.json - Add external reference
{
"externals": {
"corporate-utilities": {
"path": "https://tenant.sharepoint.com/sites/appcatalog/ClientSideAssets/corporate-utilities.js"
}
}
}
// In your web part
import { DataService, IListItem } from 'corporate-utilities';
export default class MyWebPart extends BaseClientSideWebPart<IMyWebPartProps> {
private dataService: DataService;
protected onInit(): Promise<void> {
this.dataService = new DataService(
this.context.spHttpClient,
this.context.pageContext.web.absoluteUrl
);
return super.onInit();
}
private async loadData(): Promise<void> {
const items = await this.dataService.getListItems('Announcements');
console.log('Loaded items:', items);
}
}
Microsoft Teams Integration Improvements
SPFx 1.10 enhances Teams tab development with better context awareness and improved SSO capabilities. You can now detect whether your web part is running in SharePoint or Teams and adapt the UI accordingly.
// Detecting the host environment
protected onInit(): Promise<void> {
if (this.context.sdks.microsoftTeams) {
// Running in Teams
const teamsContext = this.context.sdks.microsoftTeams.context;
console.log('Team ID:', teamsContext.teamId);
console.log('Channel ID:', teamsContext.channelId);
console.log('User Object ID:', teamsContext.userObjectId);
} else {
// Running in SharePoint
console.log('Site URL:', this.context.pageContext.web.absoluteUrl);
}
return super.onInit();
}
Performance Best Practices
With library components, bundle optimization becomes even more critical. Here are proven strategies from enterprise deployments:
1. Lazy Loading Heavy Dependencies
// Instead of importing at the top
// import { HeavyLibrary } from 'heavy-library';
// Use dynamic imports
private async loadHeavyFeature(): Promise<void> {
const { HeavyLibrary } = await import(
/* webpackChunkName: "heavy-library" */
'heavy-library'
);
const instance = new HeavyLibrary();
// Use instance...
}
2. Caching API Responses
export class CachingDataService extends DataService {
private cache: Map<string, { data: any; expiry: number }> = new Map();
private cacheDurationMs: number = 5 * 60 * 1000; // 5 minutes
public async getListItemsCached(listTitle: string): Promise<IListItem[]> {
const cacheKey = `list_${listTitle}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() < cached.expiry) {
return cached.data;
}
const items = await super.getListItems(listTitle);
this.cache.set(cacheKey, {
data: items,
expiry: Date.now() + this.cacheDurationMs
});
return items;
}
}
Common Pitfalls and Solutions
Pitfall 1: Library Version Mismatches
When you update a library component, all consuming web parts must be compatible. Use semantic versioning and test thoroughly before deploying updates.
Pitfall 2: Context Not Available in Library
Library components don’t have access to the SPFx context directly. Always pass required context (spHttpClient, siteUrl) as constructor parameters.
Pitfall 3: Teams SSO Token Expiration
Teams SSO tokens expire. Implement token refresh logic to avoid authentication failures in long-running sessions.
Key Takeaways
- Library Components reduce bundle sizes by sharing code across multiple web parts at runtime.
- Teams Integration in SPFx 1.10 provides better context awareness and SSO capabilities.
- Always pass context explicitly to library classes—they don’t have access to the SPFx context.
- Use lazy loading and caching to optimize performance in enterprise deployments.
- Plan for version compatibility when updating shared libraries.
Conclusion
SPFx 1.10 marks a maturity milestone for SharePoint Framework development. Library Components address long-standing code reuse challenges, while improved Teams integration opens new scenarios for collaborative applications. By following the patterns and practices outlined in this guide, you can build maintainable, performant solutions that scale across the enterprise. The investment in proper architecture with library components pays dividends as your solution portfolio grows.
References
Discover more from C4: Container, Code, Cloud & Context
Subscribe to get the latest posts sent to your email.