SPFx v1.13+ development: do it right from the start

kkazala

Kinga

Posted on February 5, 2022

SPFx v1.13+ development: do it right from the start

Does the word really need yet another post on how to create SPFx solutions? For sure not, and it's not my goal here. But things are changing faster than the SPFx project generator, and tips&tricks are all over the place.

This Quick Start focuses on SPFx v1.13+ projects, and references documentation (because who reads the manuals before diving head-first into development) and blogs to help you start right from the beginning.

First things first

Know your target

SPFx 1.13.1

Before setting up your SharePoint Framework development environment, decide which SharePoint and SPFx versions you want to target. Here, I'm going to focus on the latest, SPFx v1.13.1.
SPFx 1.13.1 development environment compatibility

Node Version Manager

Different SPFx versions require different node runtimes, and the easiest way to switch between them is to use Node Version Manager. This allows you installing and switching between multiple node.js and npm versions.
Don't feel tempted to update to npm v7 just yet, it's still not supported.

Package Manager

If you target SPFx v1.5+, and want to speed installation up, you may use pnpm, which will save disk space and speed installation up. Have a look here for a comparison.

Wrap up:

[ ] Install nvm for Windows or unix, macOS, and windows WSL
[ ] Install NodeJS on Windows
[ ] Install pnpm
[ ] Set up your SharePoint Framework development environment

Quick check:



> node --version
v14.15.0
> npm --version
6.14.8
> pnp --version 
6.30.0
> yo --version
4.3.0


Enter fullscreen mode Exit fullscreen mode

We are good to go!

Toolchain: good to know

@microsoft/generator-sharepoint vs @pnp/spfx

One of the npm packages included in the SharePoint Framework toolchain is
@microsoft/generator-sharepoint, a Yeoman plugin used to create new SPFx projects.
You may also consider using @pnp/spfx which extends the Microsoft's generator with additional features (VueJS, anyone? Jest? Pipelines?).

@microsoft/sp-lodash-subset

Another important one is @microsoft/sp-lodash-subset which is a custom bundle of lodash. It includes only the most essential functions.

@microsoft/office-ui-fabric-react-bundle

@microsoft/office-ui-fabric-react-bundle is a custom bundle of office-ui-fabric-react that is optimized for SharePoint Framework.

SPFx v1.13.1 is using "office-ui-fabric-react" v7. As of version 8, the office-ui-fabric-react package has been discontinued in favor of @fluentui/react but don't feel tempted to upgrade, it is recommended to use the Office UI Fabric React package included in by default.

Scaffold a new project

yo @microsoft/sharepoint options

Starting with SPFx v1.5, there's a support for additional package managers: PNPM and Yarn, with pnpm being my personal favorite:



yo @microsoft/sharepoint --package-manager pnpm


Enter fullscreen mode Exit fullscreen mode

When creating a monorepo, you may want to



yo @microsoft/sharepoint --skip-install


Enter fullscreen mode Exit fullscreen mode

to allow your monorepo build orchestrator to manage common node_modules folder and to create symlinks.

I'm going to create a Quick Start WebPart using React.

ESLint as static analysis tool for TypeScript

The generator is still using TSLint, which has been deprecated since, like, forever. It will be eventually updated, but for now we need to do the job ourselves.
To replace it with ESLint, follow the SharePoint Framework with ESLint article.
But wait, gulp-eslint7 is already deprecated as well. Use gulp-eslint-new instead:

Remember to use PNPM :



pnpm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin gulp-eslint-new eslint-plugin-react --save-dev


Enter fullscreen mode Exit fullscreen mode

And just as Sergei mentioned in his post, you will receive an error after running gulp build for a WebPart using React: error Don't use{}as a type.{}actually means "any non-nullish value.

Instead of changing {} to 'unknown', how about using functional components instead?

QuickStart.tsx



export default function QuickStart(props: IQuickStartProps) {

    return (
      //...
              <p className={ styles.description }>{escape(props.description)}</p>
      //...
    );
}


Enter fullscreen mode Exit fullscreen mode

Oh wait, but why am I still receiving tslint errors?

tslint errors

One of the packages comprising SharePoint Framework is @microsoft/sp-tslint-rules which defines custom tslint rules for usage with SharePoint client-side projects. They are still part of the solution, whilst tslint.json, disabling some of the rules, is not.
I don't feel adventurous today, I'm restoring the tslint.json.

See what changed: GitHub.

Developer information

Update developer information in the package-solution.json; if the WebPart will be used in Microsoft Teams, users will see this information when installing the app in Teams.

Ain't time to lose

Tired of waiting for the serve command to complete, after each change you make? Use spfx-fast-serve, "a command line utility, which modifies your SharePoint Framework solution, so that it runs continuous serve command as fast as possible".

After installing the spfx-fast-serve globally, navigate to your solution's folder and run the following commands:



spfx-fast-serve
pnpm install


Enter fullscreen mode Exit fullscreen mode

If you are using ESLint instead of TSLInt, you need to add the following configuration:

fast-serve/config.json



  "serve": {
    "eslint": true
  }


Enter fullscreen mode Exit fullscreen mode

Now you can simply run pnpm run serve to start working with your solution. Make a change in your code and see how fast the page will refresh. What a difference!

Remember, that SPFx 1.13+ does not support local workbench. See Debugging SPFx 1.13+ solutions

See what changed: GitHub.

Error logging

Add error logging with @pnp/logging. Define Log level as a property to enable Verbose logging only when needed.



pnpm install @pnp/logging 


Enter fullscreen mode Exit fullscreen mode

Errors returned by @pnp/sp commands are handled using Logger.error(e), which parses and logs the error message. If the error message should be displayed in the UI, use custom errorhandler function implemented based on Reading the Response example.
Users shouldn't see this setting, as it provides no value for them. Simply don't include it in the property pane.

QuickStartWebPart.ts



import {
  ConsoleListener, Logger
} from "@pnp/logging";

export interface IQuickStartWebPartProps{
  logLevel?: number;
}
export default class QuickStartWebPartextends BaseListViewCommandSet<IQuickStartWebPartProps> {
  @override
  public onInit(): Promise<void> {
    Logger.subscribe(new ConsoleListener());
    if (this.properties.logLevel && this.properties.logLevel in [0, 1, 2, 3, 99]) {
      Logger.activeLogLevel = this.properties.logLevel;
    }
    Logger.write(`${LOG_SOURCE} Initialized QuickStartWebPart`);  
    return Promise.resolve();
  }
}


Enter fullscreen mode Exit fullscreen mode

You may define the logLevel to error (3) in WebPart's manifest:

QuickStartWebPart.manifest.json



{
  "preconfiguredEntries": [{
  //... 
    "properties": {
      "description": "QuickStart",
      "logLevel":3
    }
  }]
}


Enter fullscreen mode Exit fullscreen mode

and set it to verbose (0) when troubleshooting is needed using Set-PnPClientSideWebPart.



Set-PnPClientSideWebPart -Page Home -Identity xxx -PropertiesJson '"properties": {""description"": ""QuickStart"",""logLevel"":0}'


Enter fullscreen mode Exit fullscreen mode

See what changed: GitHub

In case you are using this approach with Custom Actions, you may define the logLevel using Add-PnPCustomAction command



Add-PnPCustomAction -Title "Panel" -Name "panel" -Location "ClientSideExtension.ListViewCommandSet.CommandBar" -ClientSideComponentId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -ClientSideComponentProperties "{""logLevel"":""3""}" -RegistrationId 100 -RegistrationType List -Scope Web


Enter fullscreen mode Exit fullscreen mode

Updating the logLevel in an already deployed solution is done with:



$ca=Get-PnPCustomAction -Scope Web -Identity "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$ca.ClientSideComponentProperties="{""logLevel"":""1""}"
$ca.Update()


Enter fullscreen mode Exit fullscreen mode

See Working with modern client-side pages using PnP PowerShell for examples on how to obtain your WebPart's identity.

Missing peer dependencies

If, like me, you are using pnpm, you have seen a lot of ✕ missing peer typescript@ during pnpm install:
Missing peer dependencies
This is simply becasue pnpm is less forgiving, compared to npm or yarn. Once you add typescript to your devDependencies, the warning will dissapear:



  "devDependencies": {
    //...
    "typescript": "^3.9.0"
  }


Enter fullscreen mode Exit fullscreen mode

Imports done right

When it comes to optimizing your solution, there's one low-hanging fruit: imports.
Instead the whole library, reference only specific method(s) you want to use.
And it it's going to be used only in specific cases, use dynamic imports

Localizing

Localize your solution as described here. You can get the currentUICultureName from spPageContextInfo this.context.pageContext.legacyPageContext property.

Using different values in development and production?

Don't hard code, use this approach instead.

Using React?

Functional Components vs. Class Components

The React component created by the generator is using a class component: React.Component. Consider using functional components instead. It's cleaner, easier and use hooks.

And although there are no plans to remove classes from React, even @fluentui/react v8 converted some components from class components to function components.

Error Boundaries

Use Error Boundaries to catch JavaScript errors in the component tree, and to display a fallback UI.
There's already an react-error-boundary component you can download

Strict mode

Consider using strict mode for highlighting potential problems in an application. Strict mode checks are run in development mode only; they do not impact the production build.

Fluent UI React

Use Fluent UI's controls to build user interface, and ThemeProvider to apply contextual theming.

To style specific component using colors from current theme:



import { getTheme, Stack, IStackStyles} from "office-ui-fabric-react";
const theme = getTheme();

const stackStylesParent: IStackStyles = {
    root: {
      background: siteTheme.palette.themeLighter 
    },
};

export default function MyComponent(props: IMyComponentProps) {
  return <Stack styles={stackStylesParent} >
  </Stack >;
}



Enter fullscreen mode Exit fullscreen mode

I realize this list could go on and on, but let me stop here.
Or did I forget something really important? Please share your tips in the comments below! :)

💖 💪 🙅 🚩
kkazala
Kinga

Posted on February 5, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related