SPFx web part for hosting HTML experiences in modern SharePoint pages, with deep-link navigation, inline rendering, security controls, and deployment automation.
Security notes and current dependency-alert disposition: SECURITY.md
SPFx security-uplift spike runbook: docs/SPFx-Security-Uplift-Spike.md
- SPFx runtime target:
1.22.2packages inspfx/UniversalHtmlViewer/package.json. - SharePoint package version (
.sppkg):1.0.32.34inspfx/UniversalHtmlViewer/config/package-solution.json. - Web part manifest version:
1.0.34inspfx/UniversalHtmlViewer/src/webparts/universalHtmlViewer/UniversalHtmlViewerWebPart.manifest.json. - Node for CI/build:
22.x(see GitHub workflows and package engine constraint).
spfx-tests.yml: cross-platform lint + unit tests + bundle validation on push/PR.release-sppkg.yml: ship bundle/package build, release artifact generation, and optional GitHub Release creation onv*tags or manual dispatch.- Release packaging outputs are versioned as
release/universal-html-viewer-<manifest-version>.sppkgin CI artifacts.
Static HTML report/app bundles in SharePoint often cause iframe download behavior, broken relative links, weak deep-linking, and inconsistent page scrolling. UHV provides a predictable host layer for those experiences.
UHV is an SPFx app that delivers a reusable web part.
- It can be added to any modern SharePoint page.
- The host page name is arbitrary (
Dashboard.aspx,Reports.aspx,Ops.aspx, etc.). - It can live alongside other web parts on the same page.
- It hosts HTML content; it is not limited to "dashboards" only.
| Capability | Native SharePoint file open | UHV web part host |
|---|---|---|
| Complex HTML bundles (scripts + nested iframes) | Can render inconsistently, partially, or trigger file-download behavior depending on headers/viewer context. | SharePointFileContent or SharePointFileBlobUrl renders inline in a controlled host with consistent behavior. |
| Relative-link navigation between HTML pages | Often leaves current page context or behaves like raw file navigation. | Intercepts supported links and keeps navigation inside UHV host experience. |
| Shareable links to specific subpages | Usually tied to raw file URLs, not unified host-page state. | Uses host-page URL state (?uhvPage=...) for stable, shareable deep links. |
| Back/Forward browser behavior | Not guaranteed for embedded report-state transitions. | Managed through host URL state and history handling. |
| Initial load scroll stability on heavy pages | Can jump due to asynchronous layout and nested frame timing. | Adds host-scroll stabilization and nested iframe hydration protections. |
| URL boundary controls | General SharePoint permissions only. | Adds web-part-level URL policy (StrictTenant, Allowlist, AnyHttps). |
- Teams publishing generated report bundles (PowerShell, BI exports, static report generators).
- Operations/security teams that need stable deep-link sharing for investigations.
- Knowledge portals combining HTML apps with other SharePoint web parts on the same page.
- Any tenant needing governance-friendly hosting of custom HTML experiences without custom SPFx coding for each app.
If you are new to this repository, use this quick map:
- Product overview and configuration:
README.md - Deployment guide:
docs/Deploy-SharePointOnline.md - Reusable operations runbook:
docs/Operations-Runbook.md - Release checklist:
docs/Release-Checklist.md
Most common contributor flows:
# Build/package
.\scripts\Build-UHV.ps1
# Deploy package to app catalog
.\scripts\Deploy-UHV-Wrapper.ps1 -AppCatalogUrl "https://<tenant>.sharepoint.com/sites/appcatalog" -Scope Tenant -DeviceLogin
# One-command site setup
.\scripts\Setup-UHVSite.ps1 -SiteUrl "https://<tenant>.sharepoint.com/sites/Reports" -DeviceLoginUse ignore/ for local-only files (tenant values, private notes, temporary snippets).
Template: scripts/examples/UHV.LocalProfile.example.ps1 -> local copy: ignore/UHV.LocalProfile.ps1.
Contributor skills (repo-local playbooks): skills/README.md
- Render mode selection:
SharePointFileContent(inlinesrcdoc) orSharePointFileBlobUrl(blob:iframe).DirectUrlremains a legacy/expert option for external scenarios. - Deep-link support with shareable page URLs via
?uhvPage=...(disabled by default; enable withallowQueryStringPageOverride). - Nested iframe hydration for report wrappers.
- Extension-aware inline navigation (
.html,.htm,.aspxby default). - FRE/runtime-safe inline navigation bridge for
srcdoccontent hosted in modern SharePoint pages. - SharePoint-backed report browser source mode for folder/file discovery without creating one page per report.
- Strong URL policy controls:
StrictTenant,Allowlist,AnyHttps. - Expert-mode guardrail for unsafe security options (
AnyHttps). - Property-pane presets for fast setup (
SharePointLibraryRelaxed,SharePointLibraryFullPage,SharePointLibraryStrict). - Auto-height and width-fit behavior for large HTML pages.
- Scripted build/deploy/update/rollback workflows.
This is the real journey users and admins follow in SharePoint.
For curated screenshot-friendly demo content, see:
samples/siteassets/UHV-Screenshot-Demo.htmlsamples/siteassets/UHV-Feature-Showcase.htmlsamples/siteassets/README.md
You can quickly confirm install status before touching any page configuration.
Once added, UHV becomes the host layer for your HTML app/report experience.
Set report source and use SharePointFileContent for inline rendering reliability.
Use published view (optionally ?env=Embedded) for a clean app-like experience.
The showcase can document deep-link contract, policy modes, and deployment runbooks in one surface.
Published page view is ready for end users. Optional page social actions can be disabled at site scope.
flowchart LR
A[Install app] --> B[Add web part]
B --> C[Configure source]
C --> D[Set layout/security]
D --> E[Users navigate reports]
E --> F[URL updates with uhvPage]
F --> G[Back/Forward works]
Use this as a quick checklist when reproducing the setup from screenshots.
| Screenshot | Where | Option | Recommended value |
|---|---|---|---|
uhv-showcase-editor-quick-setup.png |
Quick setup | Configuration preset |
SharePoint library (relaxed) |
uhv-showcase-editor-quick-setup.png |
Source | HTML source mode |
Single page URL |
uhv-showcase-editor-quick-setup.png |
Source | Content delivery mode |
SharePoint file API (inline iframe) |
uhv-showcase-editor-quick-setup.png |
Source | Initial/default HTML page |
https://<tenant>.sharepoint.com/sites/<site>/Shared%20Documents/<entry>.html |
uhv-source-mode-single-page.png |
Source | Initial HTML source mode |
Single page URL |
uhv-source-mode-selector.png |
Source | Initial HTML source mode |
Choose Single page URL for one file or SharePoint report browser folder for folder browsing. |
uhv-source-mode-report-browser.png |
Source | Initial HTML source mode |
SharePoint report browser folder |
uhv-showcase-runtime-embedded.png |
Runtime shell | ?env=Embedded |
Optional (when supported by host context) |
uhv-showcase-deeplink-security-ops.png |
Runtime behavior | URL state |
?uhvPage=<encoded-target> |
uhv-showcase-deeplink-security-ops.png |
Security | URL policy mode | StrictTenant or Allowlist |
uhv-showcase-runtime-socialbar.png |
Site UX | Social bar | Optional; can be disabled per site |
Use Single page URL when the web part should render exactly one configured HTML entry page. This mode does not show the Explorer/report browser, even if an older saved page still contains the legacy showReportBrowser flag.
Use the source-mode picker to switch deliberately between a single page, a SharePoint report browser folder, or the older base-path URL builders.
Use SharePoint report browser folder when one UHV host page should let users browse the reports they can access under a configured SharePoint folder. The picker respects SharePoint permission trimming and UHV URL validation.
Previous dashboard-oriented visuals are preserved for reference:
assets/legacy/uhv-dashboard-overview.pngassets/legacy/uhv-dashboard-menu.pngassets/legacy/uhv-property-pane-quick-setup.pngassets/legacy/uhv-property-pane-layout-display.pngassets/legacy/uhv-property-pane-security-iframe.png
flowchart LR
A[SharePoint page] --> B[UHV web part]
B --> C{Content delivery mode}
C -->|DirectUrl| D[iframe src]
C -->|SharePointFileContent| E[Read file from SharePoint API]
E --> F[iframe srcdoc]
D --> G[Target HTML content]
F --> G[Target HTML content]
G --> H[Inline navigation + nested iframe hydration]
sequenceDiagram
participant U as User
participant P as HostPage.aspx
participant W as UHV Web Part
participant S as SharePoint File API
U->>P: Open page with ?uhvPage=...
P->>W: Render web part
W->>S: Load target HTML
S-->>W: HTML content
W->>W: Inject srcdoc + wire inline nav
W->>W: Hydrate nested iframes
W->>P: Keep host scroll pinned to top until layout settles
| Setting | Options | Purpose |
|---|---|---|
htmlSourceMode |
FullUrl (Single page URL), SharePointReportBrowser (SharePoint report browser folder), BasePathAndRelativePath, BasePathAndDashboardId |
Defines whether UHV renders one configured page, shows a SharePoint-backed report browser, or builds the target URL from legacy base-path settings. |
contentDeliveryMode |
SharePointFileContent, SharePointFileBlobUrl, DirectUrl (legacy / external only) |
Chooses inline file content from SharePoint API via srcdoc/blob: iframe, or a direct iframe URL for hosts that allow it. SharePoint .html files usually download in direct mode, so use an inline SharePoint mode for SharePoint libraries. |
queryStringParamName |
string | Query key used for ID/path source mode. |
defaultFileName |
string | Default file when the requested id/path is missing. |
reportBrowserRootPath |
folder path | Root folder to enumerate with SharePoint REST security trimming when htmlSourceMode is SharePointReportBrowser. |
reportBrowserDefaultView |
Folders, Files |
Starts the picker in folder navigation or recursive file-list mode. |
reportBrowserMaxItems |
300 |
Limits recursive file-list results to avoid expensive folder scans. |
| Setting | Typical value | Purpose |
|---|---|---|
heightMode |
Auto |
Auto-fit to content height (recommended for reports). |
fixedHeightPx |
800-1000 |
Minimum visual baseline in auto mode. |
fitContentWidth |
true |
Shrinks wide report content to frame width. |
showChrome |
true |
Top header with status/actions. |
showOpenInNewTab |
true |
Gives fallback path to open raw report page. |
| Setting | Options | Purpose |
|---|---|---|
securityMode |
StrictTenant, Allowlist, AnyHttps |
URL policy boundary. |
enableExpertSecurityModes |
true / false |
Required to enable unsafe expert options such as AnyHttps. |
allowedHosts |
host list | Explicit host allowlist for Allowlist mode. |
allowedPathPrefixes |
path list | Optional path constraints for tighter scope. |
allowQueryStringPageOverride |
true / false |
Allows or disables uhvPage query-driven deep-link override in inline mode. |
inlineDeepLinkParamName |
query parameter name | Defaults to uhvPage. Give each viewer a unique name when a page contains more than one UHV web part. |
inlineExternalScripts |
true / false |
Compatibility mode that fetches allowed external report scripts and inlines them before rendering. Useful when SharePoint CSP blocks CDN script tags used by generated reports. |
inlineExternalScriptAllowedHosts |
host list | Optional host allowlist for external script inlining. When empty, UHV allows common PSWriteHTML CDN hosts: code.jquery.com, cdnjs.cloudflare.com, cdn.jsdelivr.net, cdn.datatables.net, nightly.datatables.net, unpkg.com. |
sandboxPreset |
preset or custom | Controls iframe sandbox behavior. |
iframeAllow |
permissions policy string | Optional iframe permissions (fullscreen, etc.). |
- In
SharePointFileContentmode, UHV injects a defensiveContent-Security-Policymeta tag intosrcdocwhen the source HTML does not define one. Strictisolates report content in an opaque origin while allowing user-initiated downloads and new tabs.Relaxedsupports reports that need same-origin DOM access, but it should be used only for trusted content becauseallow-scriptsplusallow-same-originis not an effective containment boundary.- If your report HTML already defines a CSP meta tag, UHV preserves that policy and does not inject a second one.
inlineExternalScriptsis intentionally opt-in because it executes the same third-party scripts the report references, but from inline HTML instead of blocked CDN<script src>tags.
- Preset:
SharePointLibraryRelaxed - Source mode:
Single page URLfor one entry page, orSharePoint report browser folderfor browsing a folder tree - Content delivery:
SharePointFileContent - Height mode:
Auto - Fit content to width:
On - Keep reports and linked pages in same tenant/site boundary
- Avoid
AnyHttpsunless you explicitly accept cross-host embedding risk. AnyHttpsis available only whenenableExpertSecurityModesis enabled.
When reports live under one SharePoint folder tree, prefer one UHV host page plus deep links instead of provisioning one page per report file.
- For a single fixed report entry page, choose
Single page URLand set one default entry file. - For end-user folder/file discovery, choose
SharePoint report browser folderand setreportBrowserRootPath. - Enable
allowQueryStringPageOverride. - Constrain
allowedPathPrefixesto the report root you want UHV to serve. - Share report links as
.../SitePages/Reports.aspx?uhvPage=<encoded-server-relative-path>. - For mixed permissions, use one host page per secured folder boundary when possible, not per file.
The SharePoint report browser folder source mode lists report files from SharePoint as the current user. SharePoint permissions are not bypassed: users only see folders/files that the SharePoint REST API returns for them, and opening a file still goes through UHV URL validation.
Foldersview shows folders and report files in the current folder, with parent-folder navigation.Filesview recursively searches fromreportBrowserRootPathand shows a flat searchable list.Single page URLnever renders the Explorer/report browser. The oldshowReportBrowserproperty is treated as legacy saved state and is not the user-facing switch.- If a user can open one specific file but SharePoint does not allow them to enumerate its parent folder, the file cannot be discovered from the browser. Use a direct
uhvPagelink for that edge case.
UHV treats the host SharePoint page URL as the navigation state for the embedded HTML content.
- Base page (default entry file):
https://<tenant>.sharepoint.com/sites/<site>/SitePages/HostPage.aspx
- Deep-linked subpage/file:
https://<tenant>.sharepoint.com/sites/<site>/SitePages/HostPage.aspx?uhvPage=%2Fsites%2F<site>%2FSiteAssets%2FReportA.html
uhvPagepoints to the target HTML file to render inside UHV.- Value is URL-encoded.
- Invalid/unsafe values (control chars, backslashes, oversized payloads) are rejected.
- Works with site-relative paths (recommended) and allowed absolute URLs (based on security mode).
- If
uhvPageis missing, UHV falls back to configured default file. - By default, UHV ignores
uhvPageand keeps configured default URL unlessallowQueryStringPageOverrideis enabled. - If a page contains multiple UHV web parts, set a different
inlineDeepLinkParamNameon each viewer so Back/Forward and shareable links address the intended viewer. - In
AnyHttpsmode, UHV intentionally ignoresuhvPageoverrides and keeps configured default URL to reduce open-redirect style abuse.
flowchart LR
A[User opens host page] --> B{uhvPage present?}
B -->|No| C[Load default file]
B -->|Yes| D[Decode uhvPage]
D --> E[Validate by security mode and allowed paths]
E --> F[Load requested report file]
When allowQueryStringPageOverride is enabled, UHV updates the browser URL as users click inline report links, so browser history works naturally.
- Click inside embedded HTML link/menu:
- UHV intercepts eligible link and keeps navigation inline.
- Host URL is updated with
?uhvPage=....
- Press browser Back/Forward:
- UHV reads current
uhvPage. - Correct report file is reloaded inline.
- No full navigation away from the host page.
- UHV reads current
sequenceDiagram
participant U as User
participant B as Browser History
participant H as Host page (UHV host)
participant I as Embedded HTML content
U->>I: Click report link
I->>H: Intercept + resolve target page
H->>B: pushState(?uhvPage=target)
H->>I: Render target inline
U->>B: Back
B->>H: popstate with previous ?uhvPage
H->>I: Re-render previous report inline
- Single source of truth (when query override is enabled):
- URL query parameter (
uhvPage) represents current embedded subpage.
- URL query parameter (
- Controlled inline navigation:
- UHV only intercepts primary-click navigation to approved HTML extensions inside the configured host/path boundary. Eligible HTML anchors, including runtime-generated calendar links, are rewritten to the UHV host page so SharePoint cannot turn them into attachment downloads.
_blankstill opens a new tab, but that tab loads the UHV deep link. External links and non-HTML downloads keep native browser behavior.
- UHV only intercepts primary-click navigation to approved HTML extensions inside the configured host/path boundary. Eligible HTML anchors, including runtime-generated calendar links, are rewritten to the UHV host page so SharePoint cannot turn them into attachment downloads.
- Security-gated loading:
- All requested targets pass URL policy checks (
StrictTenant,Allowlist,AnyHttps).
- All requested targets pass URL policy checks (
- Host-scroll protection during hydration:
- Initial deep-link render temporarily locks host scroll until layout stabilizes.
- Nested iframe handling:
- UHV resets nested iframe scroll context during hydration to reduce jumpy first paint.
- With query override enabled, deep links are represented by
?uhvPage=<encoded-site-relative-or-absolute-path>. - UHV enforces top positioning during initial deep-link render.
- Scroll lock now waits for host/iframe stability and nested iframe hydration before release.
- If debugging is needed, append
?uhvTraceScroll=1and inspect[UHV scroll trace]console events.
- UHV does not bypass SharePoint permissions.
- Access is evaluated from the viewer perspective for:
- the SharePoint page containing UHV
- the underlying report files/folders being loaded
- If user can open the page but not the target file, content load fails according to SharePoint security response.
- Shareable deep links still work only for users who have permission to both page and target file.
SharePointFileContentmode loads HTML through SharePoint REST (GetFileByServerRelativePath(decodedUrl=...)/$value), which correctly handles literal%and#characters in SharePoint names.- With frequent refresh intervals and high traffic, this can increase API pressure.
- UHV now retries transient SharePoint API failures (
429,502,503,504) with bounded backoff and honorsRetry-Afterwhen provided. - Auto-refresh skips hidden browser tabs and avoids overlapping refresh runs, reducing unnecessary background API traffic.
- Recursive Files view has a bounded SharePoint request budget; if a very large folder tree reaches it, narrow the configured report root instead of issuing an unbounded request chain.
- Keep
refreshIntervalMinutesconservative and preferFileLastModifiedcache-busting over aggressive timestamp refreshes. - Tune
inlineContentCacheTtlSeconds(default 15s) to balance freshness and API volume.
- UHV provides an iframe host shell, but accessibility of rendered report content depends on the HTML inside that iframe.
- Set a meaningful
iframeTitle, use semantic HTML in report pages, and validate embedded content against your WCAG baseline.
- Use a published page view (not edit mode) for a clean runtime surface.
- Add
?env=Embeddedto page URLs to reduce SharePoint chrome where supported. - Turn off page comments in Page details when you do not need discussion threads.
- Disable Social Bar (
Like,Save for later, views) at site scope:
Import-Module Microsoft.Online.SharePoint.PowerShell
Connect-SPOService -Url "https://<tenant>-admin.sharepoint.com"
Set-SPOSite -Identity "https://<tenant>.sharepoint.com/sites/<site>" -SocialBarOnSitePagesDisabled $true- Re-enable Social Bar later if needed:
Set-SPOSite -Identity "https://<tenant>.sharepoint.com/sites/<site>" -SocialBarOnSitePagesDisabled $false- If you are already using PnP admin auth and cannot use
Connect-SPOService, site-scope CSOM fallback:
Connect-PnPOnline -Url "https://<tenant>-admin.sharepoint.com" -DeviceLogin -ClientId "<client-guid>" -Tenant "<tenant>.onmicrosoft.com"
$ctx = Get-PnPContext
$tenant = [Microsoft.Online.SharePoint.TenantAdministration.Tenant]::new($ctx)
$siteProps = $tenant.GetSitePropertiesByUrl("https://<tenant>.sharepoint.com/sites/<site>", $true)
$ctx.Load($siteProps); $ctx.ExecuteQuery()
$siteProps.SocialBarOnSitePagesDisabled = $true
$siteProps.Update(); $ctx.ExecuteQuery()- Editors/owners may still see authoring commands (
New,Promote,Edit) because those are permission-driven.
Full deployment guide: docs/Deploy-SharePointOnline.md
Operations runbook (reusable): docs/Operations-Runbook.md
# Optional: load your local (non-committed) profile values
. .\ignore\UHV.LocalProfile.ps1
# Build package
.\scripts\Build-UHV.ps1
# One-command site setup (recommended)
.\scripts\Setup-UHVSite.ps1 `
-SiteUrl "https://<tenant>.sharepoint.com/sites/Reports" `
-SiteRelativeDashboardPath "SiteAssets/Index.html" `
-ConfigurationPreset "SharePointLibraryRelaxed" `
-ContentDeliveryMode "SharePointFileContent" `
-DeviceLogin
# Build + deploy to tenant app catalog
.\scripts\Deploy-UHV-Wrapper.ps1 `
-AppCatalogUrl "https://<tenant>.sharepoint.com/sites/appcatalog" `
-Scope Tenant `
-DeviceLogin `
-ClientId "<client-guid>" `
-Tenant "<tenant>.onmicrosoft.com" `
-TenantAdminUrl "https://<tenant>-admin.sharepoint.com"
# Update installed app on sites
.\scripts\Update-UHVSiteApp.ps1 `
-SiteUrls @(
"https://<tenant>.sharepoint.com/sites/SiteA",
"https://<tenant>.sharepoint.com/sites/SiteB"
) `
-InstallIfMissing `
-DeviceLogin `
-ClientId "<client-guid>" `
-Tenant "<tenant>.onmicrosoft.com"
# All-in-one deploy/update pipeline (tenant + target sites)
.\scripts\Deploy-UHV-All.ps1 `
-AppCatalogUrl "https://<tenant>.sharepoint.com/sites/appcatalog" `
-TenantAdminUrl "https://<tenant>-admin.sharepoint.com" `
-SiteUrls @(
"https://<tenant>.sharepoint.com/sites/SiteA",
"https://<tenant>.sharepoint.com/sites/SiteB"
) `
-ClientId "<client-guid>" `
-Tenant "<tenant>.onmicrosoft.com" `
-DeviceLoginDeploy-UHV-All.ps1 validates that -AppCatalogUrl, -TenantAdminUrl, and -SiteUrls are absolute HTTPS URLs, and enforces host consistency between app catalog and target site URLs.
Deploy-UHV-All.ps1, Setup-UHVSite.ps1, and Add-UHVPage.ps1 support -WhatIf for dry-run planning.
Note: -SiteRelativeDashboardPath is a backward-compatible name and accepts any HTML entry file path.
| Script | Purpose |
|---|---|
scripts/Build-UHV.ps1 |
Build/package with local Node bootstrap fallback. |
scripts/Deploy-UHV.ps1 |
Upload/publish .sppkg to app catalog. |
scripts/Deploy-UHV-Wrapper.ps1 |
Build + deploy wrapper. |
scripts/Deploy-UHV-All.ps1 |
Build, deploy to app catalog, then update/install on target sites. |
scripts/Setup-UHVSite.ps1 |
Install/update app and provision configured page. |
scripts/Add-UHVPage.ps1 |
Add/configure UHV web part on a site page. |
scripts/Update-UHVSiteApp.ps1 |
Update installed app on one or more sites. |
scripts/Rollback-UHV.ps1 |
Roll back to older package and reapply site updates. |
scripts/examples/UHV.LocalProfile.example.ps1 |
Template for local auth/tenant profile values. |
Use ignore/ for local notes, secrets, and machine-specific snippets.
- Folder is intentionally ignored by git.
- Keep reusable templates in
scripts/examples/. - Copy template to
ignore/and edit locally:
Copy-Item .\scripts\examples\UHV.LocalProfile.example.ps1 .\ignore\UHV.LocalProfile.ps1Scripts support auth fallbacks from environment variables:
UHV_CLIENT_IDUHV_TENANT
- Report downloads instead of rendering: switch to
SharePointFileContentorSharePointFileBlobUrl. - Navigation not staying inline: verify relative links and allowed extensions.
- Deep-link opens but landing position is wrong: retest with
?uhvTraceScroll=1and review trace. - Page editing issues (
SavePageCoAuth 400): often SharePoint authoring state; see deployment guide.
If inline links suddenly start downloading HTML files after a SharePoint service/runtime update, check browser console for errors similar to:
Error: old FRE behavior is disabled
Typical impact:
- top-menu links (for example
Office365 -> Apps) trigger a file download instead of inline navigation - behavior appears intermittent across menus/widgets depending on event propagation path
Mitigation included in newer UHV builds:
- UHV injects an inline navigation bridge script into
srcdocHTML - bridge captures
pointerdown/mousedown/click, resolves target URLs against document base, and posts navigation intent to UHV parent host - parent host validates URL policy/extension/path and performs safe inline navigation
Operational checks:
- use
SharePointFileContentmode - keep
securityModeasStrictTenant(or a deliberate allowlist policy) - hard refresh host page after app upgrade
- verify site app is on current version (
InstalledVersion == AppCatalogVersion)
.
├─ assets/
├─ samples/
│ └─ siteassets/
├─ docs/
│ ├─ Deploy-SharePointOnline.md
│ └─ Operations-Runbook.md
├─ ignore/ (local-only, non-committed workspace)
├─ scripts/
│ └─ examples/
└─ spfx/
└─ UniversalHtmlViewer/








