Skip to content

Project structure

File-path reference for the CLI. For commands see commands.md; for pipeline see architecture.md; for APIs see internal-api.md.

Tree

my-app/
├── vite.config.ts | rspack.config.ts | rsbuild.config.ts  # optional — synthesized if missing
├── package.json / tsconfig.json / .env / .gitignore
├── assets/favicon.svg                                      # dev-only, served at /assets/favicon.svg
├── config/package-solution.json / serve.json / write-manifests.json / config.json
├── src/webparts/<name>/<name>.manifest.json + <name>WebPart.ts|tsx
├── src/extensions/<name>/<name>.manifest.json + <Pascal>Extension.ts
├── src/libraries/<name>/<name>.manifest.json + <Pascal>Library.ts
├── teams/manifest.json + *_color.png + *_outline.png        # when teams.enabled
├── sharepoint/assets/ + Resources.resx                      # optional
├── dist/ / release/manifests/ / release/assets/ / temp/manifests.js
├── sharepoint/solution/<name>.sppkg
└── .rspfx/ + .rspack-cache/ + ~/.rspfx/certs/

Relocatable roots (src, src/webparts, src/extensions, src/libraries, config) have defaults in core and are overridden via paths in plugin options. See internal-api.md for resolvePathDefaults() and readProject().

File table

PathPurposeRequiredCreated
vite.config.ts / rspack.config.ts / rsbuild.config.tsBundler config host (rspfxVite() / RspfxPlugin / rspfxRsbuild()). Synthesized from manifests if missing. Add to any starter (create-vite, better-t-stack, TanStack Router, etc.).Nonpm create vite@latest (or pnpm/yarn/bun/deno) + rspfxVite() or rspfx new / rspfx migrate
package.jsonname/version (AMD _<version> source).YesScaffolded
tsconfig.jsonstrict, bundler, jsx per framework.YesScaffolded
.envDotenv loaded before serve.json expansion.NoUser-provided
assets/favicon.svgDev-only favicon at /assets/favicon.svg.NoScaffolded
config/package-solution.jsonSolution id/version/features/paths.zippedPackage.YesAuto-created if missing
config/serve.jsoninitialPage ({tenantdomain}), https/port/hostname. Precedence: CLI flags → serve.jsondev.* → defaults (:4321/localhost/https).YesAuto-created
config/write-manifests.jsoncdnBasePath for release/manifests URLs.YesAuto-created
config/config.jsonbundles / externals / localizedResources ({locale}.js). Scan fallback if bundles absent.NoAuto-created
src/webparts/<name>/<name>.manifest.jsonid/alias/componentType: WebPart/version: "*".One per web partScaffolded
src/webparts/<name>/<name>WebPart.tsEntrypoint (index.ts wins — see naming rules).One per folderScaffolded
src/extensions/<name>/<name>.manifest.jsoncomponentType: Extension, extensionType.One per extensionScaffolded
src/libraries/<name>/<name>.manifest.jsoncomponentType: Library, alias, version: "*"One per libraryScaffolded
teams/manifest.jsonTeams v1.13 manifest (when teams.enabled).NoAuto-created
sharepoint/Resources.resxLocalized metadata ($Resources:KeyLocalizedString).NoUser-provided
local/data.jsonMock /_api seed for local preview.NoUser-provided
dist/AMD bundles [name].js + chunks + locale modules.Build outputrspfx build
release/manifests/ + release/assets/Production manifests + assets for packaging.Build outputassembleRelease()
temp/manifests.jsDebug manifests served at /temp/manifests.js.Dev outputDev server
sharepoint/solution/<name>.sppkgDEFLATE zip (path from paths.zippedPackage).Package outputrspfx package
~/.rspfx/certs/Self-signed certs for https://localhost:4321.Dev certsensureCertificates()

Naming rules

RuleDetail
Folder = bundleNameScan mode: src/webparts/<name>/dist/<name>.jsloaderConfig.entryModuleId = "<name>". With explicit config.json bundles, the bundle key wins.
One *.manifest.json per folderConvention <name>.manifest.json. Two files → MULTIPLE_MANIFESTS error. Zero → folder skipped.
Entrypointindex.ts/tsx<name>WebPart.ts/tsx<name>ApplicationCustomizer/FieldCustomizer/CommandSet/Extension<name>Library.ts → lone *.ts/tsx fallback. See internal-api.md for pickEntrypoint().
id + versionid is UUID, globally unique. version: "*" replaced with package.json version (pre-release stripped). AMD library is <id>_<version>. Must match teams/manifest.json id/entityId when Teams enabled.
Library / ExtensionLibrary: componentType: Library, no preconfiguredEntries, packaged as Library_<id>.xml. Extension: extensionType required, packaged as Extension_<id>.xml. Discovery merges all three dirs.

Full API signatures (readProject, discoverWebParts, pickEntrypoint) → internal-api.md. Format guarantees → compatibility.md. Bundling details → building-packages.md.

SchemaURLLearn companion
Web part manifesthttps://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.jsonWorking with web part manifests
Extension manifesthttps://developer.microsoft.com/json-schemas/spfx/client-side-extension-manifest.schema.jsonExtensions overview
Library manifesthttps://developer.microsoft.com/json-schemas/spfx/client-side-library-manifest.schema.jsonLibrary component overview
config.jsonhttps://developer.microsoft.com/json-schemas/spfx-build/config.1.0.schema.jsonSharePoint Framework toolchain
package-solution.jsonhttps://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.jsonPackage and deploy SPFx solutions
serve.jsonhttps://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.jsonServe your web part in a workbench
write-manifests.jsonhttps://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.jsonHost SPFx from Office 365 CDN
Teams v1.13https://developer.microsoft.com/json-schemas/teams/v1.13/MicrosoftTeams.schema.jsonIntegrate with Microsoft Teams

Tip: keep src/webparts/<name>/<name>.manifest.json and folder name identical. It avoids ordering surprises, keeps entryModuleId stable, and matches what rspfx migrate expects when rewriting lib → src.

Released under the MIT License.