Interaction 1 (Baseline v1.0.0): Build a Generic Copilot Prompt Hub SPFx Web Part for SharePoint
Why this exists
Most organizations try Copilot in SharePoint and quickly hit the same issue:
- People ask inconsistent questions.
- Outputs vary wildly depending on how the prompt is phrased.
- Adoption stalls because users don’t have a repeatable way to “prompt well.”
Before you build agents, connect to business lists, or automate actions, the fastest win is a prompt standardization layer embedded directly in SharePoint pages.
Interaction 1 is that layer: a Generic Copilot Prompt Hub web part that works on any modern page and provides curated prompt templates users can copy/paste into Copilot.
This approach uses the SharePoint Framework (SPFx), Microsoft’s recommended extensibility model for modern SharePoint experiences.
Primary references (Microsoft Learn):
- SharePoint Framework overview:
https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview - Build your first SPFx web part (Hello World):
https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part - Set up SPFx development environment:
https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment - Integrate with the property pane:
https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/basics/integrate-with-property-pane - Serve your web part in SharePoint (package/deploy):
https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/serve-your-web-part-in-a-sharepoint-page
What you will build (Scope of Interaction 1)
A Prompt Hub web part with:
- A left panel containing prompt templates (click to select).
- A right panel showing a prompt preview (read-only).
- Buttons:
- Copy prompt
- Copy with context (adds a short generic context header)
- Templates stored in the Property Pane as JSON (
templatesJson) so prompt content can be updated without changing code.
Important: this baseline is not an agent and does not integrate with lists, Graph, or external APIs. It is purely a UI accelerator for Copilot usage.
Prerequisites
- A working SPFx developer environment (Node + Yeoman + Gulp)
- A SharePoint Online tenant/site where you can test
- Optional: access to the tenant app catalog to deploy the
.sppkg
Microsoft Learn guidance:
- SPFx dev environment setup:
https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment
Architecture (simple by design)
Interaction 1 is intentionally minimal:
- Property Pane JSON → templates are configured by the page editor/admin.
- React UI → renders the template list + prompt preview.
- Clipboard API → copies selected prompt to the user’s clipboard.
This design avoids early friction:
- No REST calls
- No list schema mapping
- No permissions troubleshooting
- Fast deployment and immediate value
Step-by-step implementation
Step 1 — Scaffold the SPFx solution
Create a new project folder and run:
yo @microsoft/sharepoint
Recommended selections:
- Framework: React
- Web part name: CopilotPromptHub
Reference:
- Build a Hello World SPFx web part:
https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part
Step 2 — Run locally (Workbench)
Start the local dev server:
gulp serve
Add the web part to the workbench and confirm:
- It renders
- The property pane opens
SPFx toolchain reference:
Step 3 — Implement baseline Prompt Hub behavior
Baseline rules:
- Templates must be read from
templatesJson(Property Pane) - JSON parsing must be safe (show a friendly error on invalid JSON)
- Each template must contain:
id,title,prompt - Copy buttons must work consistently
Property pane integration reference:
Baseline v1.0.0 source code (copy/paste ready)
1) ICopilotPromptHubProps.ts
export interface IPromptTemplate { id: string; title: string; prompt: string;}export interface ICopilotPromptHubProps { description: string; templatesJson: string; // JSON array of IPromptTemplate}
2) CopilotPromptHubWebPart.ts
import * as React from 'react';import * as ReactDom from 'react-dom';import { Version } from '@microsoft/sp-core-library';import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';import CopilotPromptHub from './components/CopilotPromptHub';import { ICopilotPromptHubProps } from './components/ICopilotPromptHubProps';export interface ICopilotPromptHubWebPartProps { description: string; templatesJson: string;}export default class CopilotPromptHubWebPart extends BaseClientSideWebPart<ICopilotPromptHubWebPartProps> { public render(): void { const element: React.ReactElement<ICopilotPromptHubProps> = React.createElement( CopilotPromptHub, { description: this.properties.description, templatesJson: this.properties.templatesJson } ); ReactDom.render(element, this.domElement); } protected onDispose(): void { ReactDom.unmountComponentAtNode(this.domElement); } protected get dataVersion(): Version { return Version.parse('1.0'); } private getDefaultTemplates(): string { const defaults = [ { id: "status-summary", title: "Write a status summary", prompt: "Summarize the current page in 6 bullet points.\n" + "Then propose the next 3 actions with owners and due dates (use placeholders if unknown)." }, { id: "extract-decisions", title: "Extract decisions & action items", prompt: "From this page, extract:\n" + "1) Decisions made\n" + "2) Action items (who/what/when)\n" + "3) Open questions\n" + "Return as a structured list." }, { id: "risk-review", title: "Risk review", prompt: "Review this content and identify:\n" + "- Risks\n" + "- Dependencies\n" + "- Missing information\n" + "Provide a short mitigation plan." } ]; return JSON.stringify(defaults, null, 2); } protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { return { pages: [ { header: { description: "Copilot Prompt Hub settings" }, groups: [ { groupName: "General", groupFields: [ PropertyPaneTextField('description', { label: 'Description' }) ] }, { groupName: "Prompt Templates", groupFields: [ PropertyPaneTextField('templatesJson', { label: 'Templates JSON', multiline: true, rows: 18, value: this.properties.templatesJson || this.getDefaultTemplates() }) ] } ] } ] }; }}
3) CopilotPromptHub.tsx
import * as React from 'react';import styles from './CopilotPromptHub.module.scss';import { ICopilotPromptHubProps, IPromptTemplate } from './ICopilotPromptHubProps';export default function CopilotPromptHub(props: ICopilotPromptHubProps) { const [templates, setTemplates] = React.useState<IPromptTemplate[]>([]); const [error, setError] = React.useState<string | null>(null); const [selected, setSelected] = React.useState<IPromptTemplate | null>(null); React.useEffect(() => { parseTemplates(props.templatesJson); // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.templatesJson]); function parseTemplates(jsonText: string): void { try { setError(null); if (!jsonText || !jsonText.trim()) { setTemplates([]); setSelected(null); return; } const parsed = JSON.parse(jsonText) as IPromptTemplate[]; if (!Array.isArray(parsed)) { throw new Error("Templates JSON must be an array."); } parsed.forEach(t => { if (!t.id || !t.title || !t.prompt) { throw new Error("Each template must have id, title, and prompt."); } }); setTemplates(parsed); setSelected(parsed[0] || null); } catch (e: any) { setError(e?.message || "Invalid templates JSON"); setTemplates([]); setSelected(null); } } async function copyToClipboard(text: string): Promise<void> { await navigator.clipboard.writeText(text); alert("Prompt copied to clipboard."); } return ( <div className={styles.root}> <div className={styles.header}> <div className={styles.title}>Copilot Prompt Hub</div> <div className={styles.subtitle}> {props.description || "Reusable prompts to speed up Copilot work."} </div> </div> {error && ( <div className={styles.errorBox}> <b>Templates error:</b> {error} </div> )} <div className={styles.grid}> <div className={styles.left}> <div className={styles.sectionTitle}>Templates</div> <div className={styles.list}> {templates.map(t => ( <button key={t.id} className={`${styles.listItem} ${selected?.id === t.id ? styles.active : ''}`} onClick={() => setSelected(t)} > {t.title} </button> ))} {templates.length === 0 && !error && ( <div className={styles.empty}>No templates configured.</div> )} </div> </div> <div className={styles.right}> <div className={styles.sectionTitle}>Prompt</div> <textarea className={styles.promptBox} readOnly value={selected?.prompt || ""} placeholder="Select a template..." /> <div className={styles.actions}> <button className={styles.primaryBtn} disabled={!selected} onClick={() => void copyToClipboard(selected!.prompt)} > Copy prompt </button> <button className={styles.secondaryBtn} disabled={!selected} onClick={() => void copyToClipboard(`Context: I am working in SharePoint.\n\n${selected!.prompt}`)} > Copy with context </button> </div> <div className={styles.tip}> Tip: paste the prompt into Copilot. Better page structure (headings + bullets) improves results. </div> </div> </div> </div> );}
4) CopilotPromptHub.module.scss
.root { font-family: "Segoe UI", Arial, sans-serif; border: 1px solid #e6e6e6; border-radius: 12px; padding: 14px; background: #fff;}.header { margin-bottom: 12px; }.title { font-size: 18px; font-weight: 650; }.subtitle { font-size: 12px; color: #666; margin-top: 4px; }.errorBox { background: #fff4f4; border: 1px solid #ffd7d7; color: #5a0000; padding: 10px; border-radius: 10px; margin-bottom: 12px;}.grid { display: grid; grid-template-columns: 260px 1fr; gap: 12px;}.sectionTitle { font-weight: 600; margin-bottom: 8px; }.left { border: 1px solid #f0f0f0; border-radius: 12px; padding: 12px; background: #fafafa;}.list { display: flex; flex-direction: column; gap: 8px; }.listItem { text-align: left; border: 1px solid #ddd; background: #fff; padding: 10px; border-radius: 10px; cursor: pointer;}.active { border-color: #2b6cb0; box-shadow: 0 0 0 2px rgba(43,108,176,0.15);}.empty { color: #777; font-size: 12px; padding: 6px 0; }.right { border: 1px solid #f0f0f0; border-radius: 12px; padding: 12px; background: #fff;}.promptBox { width: 100%; height: 220px; border-radius: 10px; border: 1px solid #ddd; padding: 10px; font-size: 12px; font-family: Consolas, "Courier New", monospace;}.actions { margin-top: 10px; display: flex; gap: 10px; }.primaryBtn { padding: 9px 12px; border-radius: 10px; border: 1px solid #2b6cb0; background: #2b6cb0; color: #fff; font-weight: 600; cursor: pointer;}.secondaryBtn { padding: 9px 12px; border-radius: 10px; border: 1px solid #777; background: #fff; color: #222; font-weight: 600; cursor: pointer;}.tip { margin-top: 10px; font-size: 12px; color: #555; }
Packaging & deployment (complete the baseline)
Once your web part works locally, package it for production deployment:
gulp bundle --shipgulp package-solution --ship
Then:
- Upload the
.sppkgto your tenant app catalog - Deploy the solution
- Add the app to the target site
- Add the web part to a modern page
Reference:
Definition of Done (Interaction 1)
Your baseline is complete when:
- Web part renders on a modern page
- Property pane accepts valid JSON templates
- Invalid JSON shows a friendly error message (page does not break)
- Copy buttons copy exactly what the user sees
- Solution is packaged and deployed via the app catalog and usable on pages
What comes next (Interaction 2 preview)
Interaction 2 keeps it list-free but makes it “enterprise-ready”:
- Categories + search box
- Favorites (local storage)
- Import/export prompt sets (JSON)
- Preconfigured entries (simplify adding standard prompt packs)
Reference:
- Preconfigured entries guidance:
https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/guidance/simplify-adding-web-parts-with-preconfigured-entries
Final summary tables
Step-by-step summary
| Step | Action | Result |
|---|---|---|
| 1 | Scaffold SPFx React web part | Working SPFx project (MS Learn: Hello World) |
| 2 | Run locally (gulp serve) | Validate UI + property pane |
| 3 | Implement Prompt Hub UI + JSON parsing | Prompt templates selectable & previewable |
| 4 | Package (--ship) | .sppkg ready |
| 5 | Deploy to app catalog | Web part usable on SharePoint pages |
Technical mapping
| Component | Technology | Why it’s used |
|---|---|---|
| Web part platform | SPFx | Microsoft’s modern SharePoint extensibility model |
| UI framework | React | Common SPFx pattern supported by MS Learn tutorials |
| Configuration | Property Pane | Allows prompt updates without rebuilding |
| Copy behavior | Clipboard API | Fast workflow: select → copy → paste into Copilot |
| Deployment | App Catalog + .sppkg | Standard SPFx enterprise deployment path |
