case study 03 / 05 · spfx · version 1 live since September 2026

CRM web part

A CRM that lives inside SharePoint: leads, contacts and follow-ups in Microsoft 365

A CRM for a small consultancy, built as a SharePoint Framework web part: leads, companies, contacts, tasks and reports are stored in SharePoint lists, and the whole app is one numbered file that a small loader picks up from a library. The product design, screens and business rules come from the supervisor's HTML prototype; I decided the route, built the SharePoint layer, the loader and the setup scripts, and delivered it. Version 1 has been live since September 2026; version 2 is built and packaged.

SPFxTypeScriptSharePoint RESTPnP PowerShellesbuildVitest
15 + 2SharePoint lists, plus 2 libraries
8screens
6pipeline stages in version 2
89automated tests in the code base
Role
development, as a working student at S01 Projektmanagement GmbH: route, SharePoint layer, release, setup
Product design
the supervisor's HTML prototype: screens, business rules and look
Users
the practice owner, day to day; the supervisor approves and uploads releases
Platform
SharePoint Online in Microsoft 365, in the browser; on phones and tablets from version 2
Status
version 1 live since 29 September 2026; version 2 (described here) built, packaged and waiting for release
01 / why it exists

A prototype that kept its data in one browser

The CRM began as the supervisor's single-file HTML prototype: leads, contacts and follow-ups on one page, with the data in the local storage of one browser. There was no shared store, and a backup meant a manual export.

There was no sanctioned way to run it inside the company's Microsoft 365. SharePoint switches custom scripts off again within 24 hours, the company has no premium licence for Power Apps, and a canvas app could not reproduce the approved design. A SharePoint Framework web part is the supported route, so I compared the options and decided on it.

Enquiries arrive as e-mails and agency job ads, and typing them in again is slow, so the app reads a pasted e-mail and proposes company, contact and lead. Follow-ups used to depend on memory; now a lead without an open next step is flagged.

Who uses it

Built for one day-to-day user, the practice owner; the app has no user roles. The supervisor approves each release and is the only person who uploads one.

What it costs to run

Nothing beyond Microsoft 365: no server, no extra licence, and nothing leaves the tenant.

02 / how it works

How the web part loads, runs and saves

Architecture diagram: CRM web part A loader web part on the CRM page asks the app library for the newest app file and starts it. The app keeps its records in a store in memory and saves changes in order through a REST adapter into the lists and the files library. Provisioning scripts set up the lists, libraries and page. SPFX Loader web part picks the newest app file SHAREPOINT App library one numbered file per release TYPESCRIPT · SHADOW DOM CRM application 8 screens, two languages TYPESCRIPT Store and persister rules and ordered saves SHAREPOINT REST REST adapter writes only changed columns SHAREPOINT 15 CRM lists the database SHAREPOINT Files library a folder per record PNP POWERSHELL Provisioning scripts sets up the site MAX 3 TRIES UPLOAD = RELEASE Architecture diagram: CRM web part A loader web part on the CRM page asks the app library for the newest app file and starts it. The app keeps its records in a store in memory and saves changes in order through a REST adapter into the lists and the files library. Provisioning scripts set up the lists, libraries and page. SPFX Loader web part picks the newest app file SHAREPOINT App library one numbered file per release TYPESCRIPT · SHADOW DOM CRM application 8 screens, two languages TYPESCRIPT Store and persister rules and ordered saves SHAREPOINT REST REST adapter writes only changed columns SHAREPOINT 15 CRM lists the database SHAREPOINT Files library a folder per record PNP POWERSHELL Provisioning scripts sets up the site MAX 3 TRIES UPLOAD = RELEASE
  1. 1Set up the siteThe PnP PowerShell scripts create the lists, libraries, navigation and page. A verify script then checks the site against the schema.
  2. 2Open the CRM pageThe loader asks the app library for its files and picks the highest-numbered app file.
  3. 3Start the appThe loader runs that file inside a Shadow DOM, and the app reads every list in parallel through the REST adapter.
  4. 4Work in the CRMEdits go to the in-memory store, which checks the rules and redraws the screen at once.
  5. 5Save in the background MAX 3 TRIESThe persister sends changes to SharePoint in order, with up to three tries. If SharePoint refuses a record, it is rolled back and SharePoint's message is shown.
  6. 6Ship a new version UPLOAD = RELEASEThe supervisor drops the next numbered file into the library. The next page open runs it, and deleting it is the rollback.
03 / data model

How the records relate in SharePoint

Data model: CRM web part A company has many contacts and many leads, as end client or as intermediary. A contact is the main contact of many leads and can also be a further contact on many leads. A lead has many history entries, tasks, files and links. Quarter results are counted from the records and kept as rows of their own. Companies one per company PK App key Name (unique) Company type City Country Contacts one per person PK App key FK Company Name Contact type Rating 0–5 Leads one per enquiry PK App key Stage, priority, amount FK End client (company) FK Intermediary (company) FK Main contact History one per activity PK App key Type Text Date FK Lead Tasks one per task PK App key Title Due date, done, priority FK Record (kind + app key) Files and links one per file or link PK App key FK Owner (kind + app key) Description File or URL Quarter results one per closed quarter PK Quarter Closed at OKR score Frozen figures Review notes
  • Companies list

    one per company

    • PKApp key
    • Name (unique)
    • Company type
    • City
    • Country
    • 1 company → many contacts
    • 1 company → many leads (end client)
    • 1 company → many leads (intermediary)
  • Contacts list

    one per person

    • PKApp key
    • FKCompany
    • Name
    • Contact type
    • Rating 0–5
    • 1 contact → many leads (main contact)
    • many contacts ↔ many leads (further contacts)
  • Leads list

    one per enquiry

    • PKApp key
    • Stage, priority, amount
    • FKEnd client (company)
    • FKIntermediary (company)
    • FKMain contact
    • 1 lead → many History entries
    • 1 lead → many tasks
    • 1 lead → many files and links
  • History list

    one per activity

    • PKApp key
    • Type
    • Text
    • Date
    • FKLead
  • Tasks list

    one per task

    • PKApp key
    • Title
    • Due date, done, priority
    • FKRecord (kind + app key)
  • Files and links library

    one per file or link

    • PKApp key
    • FKOwner (kind + app key)
    • Description
    • File or URL
  • Quarter results list

    one per closed quarter

    • PKQuarter
    • Closed at
    • OKR score
    • Frozen figures
    • Review notes

Simplified: seven boxes stand for eight of the 17 SharePoint objects (Files and links combine a library and a list). Not drawn: events, market research and rate benchmarks (same pattern), the OKR list, four provisioned lists that no screen uses, and the app library (code, not data). This is version 2; version 1 had 14 lists. Dashed border: document library or file store.

One app key per record

Every record has its own app key, so SharePoint IDs stay internal and links survive renames. History, tasks, files and links point at any record by its kind plus app key.

Stable names, German labels

Technical names never change; German display names sit on top, so lists and columns can be renamed without breaking the app.

Figures are counted, then frozen

The hit ratio is the share of won leads among the won and lost leads of the period; withdrawn leads are left out. Closing a quarter freezes the figures into one row of quarter results.

04 / decisions

Trade-offs that shaped the build

A tiny loader and one versioned app file

Only a 17 KB loader web part goes through the tenant's app catalog, installed once by an administrator. The CRM itself is one numbered file in a site library: uploading it is the release, deleting it is the rollback, and schema changes only ever add. Trade-off: that library's write permission has to be locked down.

SharePoint lists as the database

Lists with stable technical names, German display names on top and an app-generated key per record: no external hosting, no server, no extra licence. Trade-off: the app has to enforce integrity itself, it assumes one writer at a time, and the last write wins.

Optimistic saving with a diff-based persister

Every change lands in the in-memory store first and the screen redraws at once; a persister then compares before and after and sends only the changed columns, in order, in the background. Trade-off: a failed save is noticed late, as a message and a rollback of that one record.

Port the prototype, don't rewrite it

No framework, a Shadow DOM and translation by dictionary, so the approved design appears exactly and later revisions of the prototype stay mergeable. Trade-off: most of the code is the ported prototype and is not type-checked.

05 / my part

What I did

  • Compared the routes (Power Apps code apps, hosted HTML, a canvas app, an external site), decided on SPFx on SharePoint lists and wrote down why.
  • Built the SharePoint layer in strict TypeScript, with the REST adapter, the persister with retries and rollback, and the file store, and tested it with 39 unit tests.
  • Built the loader web part and the release mechanism, and wrote the PnP PowerShell scripts that set up the site and verify it against the schema.
  • Ported the supervisor's prototype, which supplies the product design, screens and business rules, and kept it current from revision 18 to revision 48 and through the feedback of 14 October 2026; added SharePoint storage, German and English, the phone layout and a sandbox.
  • Extended the e-mail reader so that it also understands agency job ads.
  • Wrote the documentation (concept, handover, data model in German and English, release procedure, go-live checklist, test plan, release notes) and acted as interim site owner for the go-live.
06 / result

Version 1 live since September 2026, version 2 packaged

Version 1 has been live since 29 September 2026, 13 days after the first commit on 16 September. Version 2 was built on 14 October and packaged on 15 October 2026, and it waits for the supervisor's release. The live demo runs the version 2 code with invented data of the fictional Nordstern Projektmanagement GmbH, saved in the browser instead of SharePoint.

Open the live demo