Getting started with Redux in SharePoint Framework – Part 2

As you can read in part 1 we are converting a ‘plain’ React SPFx webpart to a version with Redux to have an external stage management mechanism.

We are starting from the basic idea of having a webpart that displays a list of sessions of a SharePoint Saturday. Next to displaying the sessions, the user can also remove a session from the list and add a new session the list.

To convert this to a working Redux application, we need to follow the next steps:

  1. Install necessary npm packages
  2. Create and configure store
  3. Create actions
  4. Create reducer
  5. Decouple components from each other (remove callback functions)
  6. Connect components to the reducer
Overview of the webpart

NPM packages

Basicly you need to install Redux with the following command

npm install redux

You also needs to add the React binding for Redux

npm install react-redux

Finally you need to install Redux-Thunk for middleware operations (more information later on this post).

npm install redux-thunk

Create and configure the Redux store

Using Redux, the store is THE place where all the application data (state) is stored. The only way to change the data is to dispatch an action and to process that action with a reducer.
Note: In comparison to other state managers Redux only has one store.

Creating the store is very simple. Create a new folder called store (not mandatory but recommended), create a new file configurestore.ts and add the following code to the file:

After creating the store, the application and components need to know that there is a store and that it can be used to save, update and retrieve data.
This needs to be done in the top level component of our component structure => in our example ReduxSessionWp.tsx. Call the configureStore() function and pass it to the Provider.

A Provider is a wrapper component that makes it possible for the nested components (the ‘normal’ components of our application) to access the store by using the connect() function.


Now that the basic one-time-only configuration part is over, we can start defining the actions. Actions are pieces of information that the application and individual components can send to the reducer to update the global application state.

An action is an object built up with 2 basic properties:

  • Type
  • Data (string, number, complex object…)

When a component wants to update the global application state, it needs to dispatch an action (for example: {Type: “Add_Session”, Data: {speaker:”Yannick”, session: “ABC”}}). The reducer will pick up the trigger and execute that specific action.

For each action that is possible within your application, you need to define it by using the ActionTypes. Basically it needs to be a const string but you can also manage it with an enum for each possible action. By using the enum you can reuse it more easily in your application.

Dispatching an action is possible by calling the corresponding function. Those functions are called action creators. The only thing they do is create an action object of type and data.


The only location where the global application state in Redux can be changed is inside the Reducer.

The reducer is composed of pure functions that respond to the actions that are sent to the store. It will take in the old state and an action and returns the new (updated) state to the store.

Note: Pure functions are functions that return the same output for the same given input, and don’t produce any side-effects.

If we take a look at the code of the reducer it ‘just’ contains a large switch statement for each possible action(Type).

First, the reducer creates a new clone of the current/old state. Next the switch decides, based on the action.type, which specific action needs to be done against the state.

Decoupling of components

In our React example some components were coupled with each other to pass data and functions from one component to another. Now with Redux, that coupling of components becomes unnecessary. We have the possibilty to individually couple/connect the components to the state.

Connecting components

At this moment the components are unaware of the global application state and can’t update it with new information. Connecting a component to the store is done in multiple steps.


This function will connect specific (you can define which ones) information from the state to properties of the component. So instead of passing data from one component to another we now connect directly to the data.

Your application state (probably) contains a lot of data/objects, so loading all those objects to every component is overkill. Redux is smart enough to provide the possibilty to only load the objects you need. In the example above we only load the sessionItems from the state.

As with the local state of components, when there is a change of the application state, every component (that is connected to that specific updated object) will immeditialy receive the updated object.


Next to passing the objects between components in the React-only example, we also passed callback functions between components. This becomes also unnecessary with Redux and mapDispatchToProps. It allows the component to load the (pure function) actions into the properties of the component. This allows the component to simply call the action by this.props.ACTION.


So now all the converting is done of the data and functions inside the component, the only thing you need to do is one simple line of code for each component (that needs to be connected):

It let’s the component listen to the store and provides all the data and functions it needs. The properties mapStateToProps and mapDispatchToProps are optional. So it is possible to have components only having dispatched actions (Form) and components only reading data from the store (SessionList).

Middleware for async calls

As you maybe noticed, the last thing we did not yet talk about, was the async call to our API.

By default actions are synchrounsly in Redux. To achieve an async action, like for example calling an API, we need to implement Middleware. There are two popular libraries available for this, I have choosen Thunk.

Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.

Here is a nice animation availble which explains this concept:

When a view (component) triggers an action, the Middleware takes care of your API calls and updates your state.

How to implement?

Maybe you noticed it already, when I configured the store, I already included the Middleware. It is actually as simple as importing applyMiddleware (redux module), importing thunk and ThunkDispatch (redux-thunk module), and passing through applyMiddleware(thunk) in the createStore function.

Next, create a new file AsyncFunctions.ts under the actions folder. This files contains the httpClient call to the API and will dispatch (after retrieving the data) the addNewSession action. This action is exactly the same as we use inside the form component.


Tadaaa! Normally after following all the previous steps, you should have a working Redux version of your application.

The initial setup looks very difficult, especially the first time, but eventually it saves you a tremendous lot of work compared to when you needed to handle all the state changes by propagating the props and functions.

Feel free to contact me by my blog, Twitter, LinkedIn.. if you have any questions or comments!

Extra references can be found here:

Getting started with Redux in SharePoint Framework – Part 1

Finally I managed to write this blog post. It took me a couple of attempts, but after my session at SPS Doha I was encouraged enough to finish the blog post and provide it to you.

This post will take you step by step into the wonderful world of Redux, React and SharePoint Framework (SPFx). It’s built up by my own experiences of a pretty large SPFx project where Redux was used to handle the states of numerous (+50) components.

Why using ‘something’ like Redux?

The states within my first SPFx projects were pretty much easy to handle and we had a good overview of how they handled changes in the components. But the latest project I did with SPFx was really different than the previous ones. This time there were just too many components to handle and too much data to pass trough between the components. This means that it would cost us a lot of time to think about how to pass properties between components and how to traverse between the different levels of components.

And that’s when Redux came in the picture. Without Redux, as an ‘external’ state management mechanism, handling those state changes would be close to impossible.

Let’s take this simple example (which will be used throughout this post) where we have an SPFx webpart that displays the sessions of SPS Doha. Besides showing the list, it also allows the user to add and remove sessions to and from the list. Pretty simple example, but it does help to explain the dependencies between the components.

Overview of the simple application

In the Webpart component we will store a list containing all the sessions. Those sessions are fetched from a specific API (async) when the webpart is loading.

The list of sessions is passed through to the ‘Session list’-component where every item is displayed with the ‘Session item’-component. That component also contains a remove icon to remove that item from the list (which is stored in the webpart component).

On the other hand we have the input form, that component simply contains two input fields (title and speaker), and a submit button. The button is the trigger to add a new item in the list (again, that list is stored in the webpart component).

As you probably know, this means that we need to pass functions between the components containing a callback to update the list in the webpart component.
For example:

Coding of (React) example

So how do we get the example of the session list working with just ‘plain’ React code in SPFx?

Webpart/Container component

In our main component we create multiple things:

  • Function to retrieve the data from the API
  • Function to add an item to the list
  • Function to remove an item from the list
  • Rendering of the subcomponents

The most important part of this component is the rendering function. Here we see the callback functions being passed on to the child components. We also see that SessionList retrieves the local state variable sessionItems. This variable/list will be updated by the callback functions _AddItem and _DeleteItem

The other code can be found here:

Form component

Next we have the form component where we can add new items to the list. When a user clicks the button, the _handleAddButtonClick function will call the callback function handleAddItem from the properties of the component. This way the data will be propagated up towards the main component.

SessionList component

Not really magic stuff in this component, just simply a .map iteration over the sessionItems and loading the SessionItem component. The only thing to notice is the propagation of the callback function.

SessionItem component

In this component we have the option to delete the specific item from the list of sessions. This is made possible by using the callback function which is provided in the props of the SessionItem component (onDeleteItem). Calling this will trigger the main component and remove the item from the state.

Overview/conclusion React example

So as you can see in the picture below, we have to propagate a lot of data and callback functions up and down to let the application respond to the user input. In this small example it is still easy to manage and sustain the global state of the application. But when you have many more levels of components and a more complex data structure, it can easily become unsustainable to manage the global state.

And that’s where Redux comes in the picture; to provide an alternative way of managing the global state. It will create a new global state ‘beside’ your application and a component can connect to it when it’s necessary to retrieve or update data. If a component has nothing to do with data, it remains independent and unaware of the global state.

Converting to Redux

In the following post I will explain step by step how to convert this example to Redux. It will basically contain the following parts:

  • Installing and creating the basic Redux parts
    • Store
    • Reducer
    • Actions
    • ApplicationState
  • Decouple components from each other
  • Connecting components to the store
  • Middleware

Getting started with Redux in SharePoint Framework ‚Äď Part 2

SPS Doha (Qatar) 2019

My first public speaking appointment of the year brought me to wonderfull Doha. First time Qatar and even the first time for me in the Middle East. After getting selected to speak at SPS Doha, I had no idea at all what to expect. But looking back, I can certainly say that it was a great experience!

With my flight arriving at 1 AM on Friday morning and already agreed to meet at 7 AM for the desert trip, I was aware it would be a very short night ūüėÄ

So at 7 AM I met Patrick Guimonet again (already met him at SPS Dakar last year) and I met Katja Jokisalo for the first time. Togheter with Patrick, his wife and daughter, and Katja we went on an amazing desert trip including a camelride, sandboaring, visiting the inland sea… And of course taking amazing pictures!

In the evening Rick Van Rousselt also arrived in Doha and joined us for a nice boat trip toward the economic center of Doha.

Saturday was of course THE day of SPS Doha, around 50 attendees showed up for an interesting day full of enthousiastic speakers talking about all the latest related to SharePoint and Office 365.

I provided my session about introducing Redux in your SPFx solution, which is based on my experiences of my current development project. (More information about Redux and SPFx can be found at: Getting started with Redux in SharePoint Framework ‚Äď Part 1)

After SPS we (of course) went to the only Belgian Cafe in Doha for some real Belgian beer and cocktails! The final part of my trip to Doha was reserved for a wonderful dinner and some shisha in good company!

Definitly feeling ready for my next trips:
15/06/2019 ‚Äď PowerSaturday Paris
22/06/2019 ‚Äď SPS Madrid

SharePoint Saturday Netherlands and Dakar

In the past two weeks, I had the pleasure to deliver a session about SharePoint Framework deployment at both SharePoint Saturday Netherlands and SharePoint Saturday Dakar.

Netherlands (Info)

I had never provided a session outside Belgium, so where can I start better than with our neighbours: The Netherlands.

On Friday evening, there was the traditional speaker dinner in a typical Dutch pancakes restaurant with pancakes more looking like pizzas (toppings like ham, pepperoni, cheese..). The food was good and the conversation and interactions with the other speakers were even better.

Saturday was a hot day (around 30¬į C), but the registered¬†attendees still chose to follow the sessions over a drink outside on a terrace. I think there were around 300 people attending the one-day event, which is quite a big group! At 11 a.m., my session started and it went very, very smooth. I loved the enthusiastic crowd, who were asking very specific questions. It was very nice and pleasant to speak for such an amazing audience, so a big thanks to all!

There were also many sponsors, each having a great booth at the expo, who gave away amazing prizes at the closing prize draw that evening.

So definitely a SharePoint Saturday that I would like to attend more in the future. Hopefully as a speaker, but otherwise also as an attendee!

Dakar (Info)

Speaking in Dakar (Senegal) isn’t really a thing that I thought I would do immediately, and most of my colleagues called me crazy, but the moment I got the opportunity, I thought: Why not?

So I booked the trip, arrived on Friday afternoon and left on Sunday evening, and made sure that I had time to visit the main city.

A local restaurant with a small gallery was the perfect place for the speaker dinner on Friday evening. I took a local specialty, dank some¬†Bissap¬†and of course talking to the other speakers made it for me a wonderful evening and the perfect start for my ‘Dakar-weekend’.

With a slow start on Saturday, people in Senegal seems to show up late on appointments, the was room overcrowded at noon. Approximately 40-50 people attended the sessions during the day.

The closing up of my ‘Dakar-weekend’ was a trip around all the different parts of Dakar, the¬†Monument de la Renaissance Africaine¬†and a super delicious lunch in a fish restaurant near the beach.

Looking back, it was a weekend that I would recommend to anyone!

Session details

I developed a SharePoint Framework solution, what to do next and how to install it automatically?

Level: 200
Track: Developer

While SharePoint Framework being the upcoming trend in 2017 for the modern SharePoint landscape, SharePoint developers not only saw their tool belt being extended but there was also a shift becoming a modern developer. With modern development some new tools were introduced for the SharePoint developers, like Gulp, Yeoman, Npm,… Development is one thing, but deploying these solutions? That’s another matter.

Uploading wsp files to SharePoint servers and deploying web applications for SharePoint Add-in’s to Azure, are already known actions.
But with SharePoint Framework, code has to be deployed to a hosting service as an Azure CDN or a SharePoint CDN/Library and the app package has to be uploaded to the app catalog (like with SharePoint add-ins). Those are new things for the ‘classic’ SharePoint developers, as also installing solutions by the use the new Application Lifecyle Management API of SharePoint which makes it possible to deploy by code.

This session will provide an overview of the deployment options to Azure and SharePoint and will provide a demo intensive part by creating a deployment tool to install/uninstall/retract/upgrade your app on specific sites and on specific site collection catalogs.


Connect to new Office 365 Group

Creating Office 365 groups has already been a feature in Office 365 for a while. But there still was a missing piece for the old project-and team sites of SharePoint Online where users wanted to ‘migrate’ their site to the capabilities of an Office 365 group.

A Tenant administrator could already do the trick of ‘groupify’¬†the site and create an Office 365 group of it. But from an end-user perspective there wasn’t really an option other than smiling to the tenant administrator.

Luckily it’s now available for end users in targeted release (or first release ūüôā ) tenants and will probably go globally available soon.


What is “Connect an Office 365 group”

Like said in the introduction, your classic SharePoint Online site will be tranformed into an Office 365 with the following additional capabilities:

  • The modern pages are activated on your site, and the home page is changed to a modern one.
  • When filling in the owners of the group, they will become the site collection administrators and are added to the owners group of the site. Group Members are added to the members group.
  • Office 365 Group features like Planner, Conversations, Calendar… are also added to the newly created group.

Tenant administrators can disable this feature by going to the tenant settings of the SharePoint environment (classic admin center), https://[TENANT]
Under the category Connections from sites to Office 365 groups one can disable the feature by setting it to 

Besides the content (documents..) of the site, are the site pages also migrated to the “new” site collection. They are still in classic mode, but they contains all the webparts that where configured. So there is no work lost of your “old” site. If you didn’t change the old homepage (home.aspx), then the “new” created site collection just contains a modern home(1).aspx, which is the new homepage of your site collection.



From an end-user perspective, you can transform your classic site to an Office 365 Group by simply selecting the gear icon and select Connect to new Office 365 Group.

Selecting Connect to new Office 365 Group will result in an overview page about what will be created by Office 365 for your site collection:


Next, you will be guided through the default steps you have to pass when creating an Office 365 Group:



Passing through all steps, will result in a modern SharePoint Online site collection and a modern homepage.



More information about this newly added feature can be found at the official documentation of Microsoft (where it is still defined as to be released) =>

OneDrive – Some files weren’t downloaded.

Recently my colleague and me were wondering why we bumped into a specific scenario where weren’t able to download (all the) files from a shared resource on OneDrive.
The only thing we were able to see is we downloaded a .zip file with some files and folders in it with an extra file (__All_Errors.txt) containing the following text:

Indeed, a description that doesn’t explain a lot. It simply says there is something wrong, but what? In OneDrive itself, there wasn’t a error or warning popping up, so were where up to our self to find out what’s wrong.

What did we upload?

First of all let’s see what we uploaded to OneDrive. Basically, it was a .zip file contains folders and files (multiple types: .txt, .exe, .html…) that was zipped by using 7-ZIP.

Resulting in the following structure in OneDrive:


After trying a lot of things (different compression methods, different compressing tools, security settings of the download link),  we came up with the following (pretty simple) resolution:

The download only results in the error file if you want to download a specific subfolder from the uploaded .zip-file. So make sure you uncheck the subfolders and files.
As you can see in the animation below, you have the possibility to only download a specific subfolder:

So simply download the whole folder/.zip instead of a subfolder until Microsoft fixes this issue.

SharePoint Application Lifecycle Management API – How to use it in your code

As described in my previous post РInstall & Uninstall SharePoint apps by code announced at ESPC 2017РMicrosoft announced an Application Lifecycle Management (ALM) API to install, uninstall, deploy, retract, upgrade, and remove your applications from the app catalog of your tenant and/or a specific site collection app catalog (available early 2018).

Microsoft documentation

This API is useful when you want to automate your deployment management of your SPFx solutions and add-ins, which you can do by using, e.g. a console application.
A real world scenario could be that you developed an SPFx solution for a specific set of sites and you want to push that solution onto these sites. Then you can create a console application which retrieves the ID of the solution (or app as it is called by Microsoft in the app catalog), and install it specific on these sites. If there was already an older version installed on the site, the API will upgrade the solution to the newest version that is available in the app catalog.

The PnP team did already a great job by providing an extension for their PnP CSOM project. It includes a new ALM class that contains an AppManager, the AppManager provides you the posibility to execute the specific calls as install/uninstall/upgrade… for a specific site.

How to develop this console application? This blog post will provide you a step by step guide to create the application with ‘real’ REST calls to the API and with the PnP CSOM extension.

By using PnP CSOM extension for the ALM API

Once you created a new console application project in Visual Studio, add the SharePointPnPCoreOnline Nuget Package and include the following references in your program.cs file:

  • OfficeDevPnP.Core.ALM
  • Microsoft.SharePoint.Client

First thing to do is to create a secure connection to SharePoint Online. With that connection, we can retrieve a valid ClientContext, which is necessary to create an AppManager object.

This can be done by using the SharePointOnlineCredentials implemantation that requires a username (string) and a password (SecureString).

Get available apps

From that moment we have the AppManager object, so we can execute several actions. To get more information about a specific app/solution, we can use the GetAvailable() method of the AppManager. This method will execute a call to the ALM API and returns a list of all available apps for the specific site of the ClientContext.

As you can notice, we are retrieving a list of AppMetadata that contains the following information:

Install a app on a specific site

If you want to install a specific app, you can choose to provide the complete AppMetadata object or the app/solution id to the install method.

Upgrade an app on a specific site

The property CanUpgrade indicates if their is a newer version available in the app catalog than the version that is installed on the site. If there is a newer version available, the upgrade method can be used to get the app/solution to the latest version.

Add solution package to tenant app catalog

Adding a solution (app package => .sppkg) can currently only being done to the tenant app catalog and not yet to a site collection app catalog. So this request has to be targeted to the tenant app catalog site:

Deploy solution package in tenant app catalog

Before the solution is avaible for installation on sites, it has to be enabled/deployed into the tenant. This is also only possible for the tenant app catalog, so not on site collection level.

Retract and remove solution package in the tenant app catalog

If your app is no longer needed on your tenant, you can use the retract and remove action to remove it.  This is also only possible for the tenant app catalog, so not on site collection level.

By using your own REST calls to the ALM API

As with the PnP chapter; once you created a new console application project in Visual Studio, you can add the SharePointPnPCoreOnline Nuget Package and include the following reference in your program.cs file:

  • Microsoft.SharePoint.Client

An extra thing here is that you have to take care of the Authorization header, cookies, URL’s, request method, and parsing all by yourself. This is more difficult but possible like shown in the following code example:

This response will contain a list of all the available apps for the specific site and further on you can use the ID (and not the whole metadata als with the PnP extension) to install/upgrade… an app.

Installing an app can be done by the url:¬†/_api/web/tenantappcatalog/AvailableApps/GetById(‘GUID’)/Install, and needs to be used in a POST request:

The other calls like adding, deploying, retracting, removing.. can definitly be done by simple REST calls, but are prety much the same as described above.


As you can see the new ALM API is really powerful and can help a lot of SharePoint teams to create an automatic deploy process for their apps. It can also help these teams to push specific apps to specific sites to increase the usage and installment rate of their apps/solutions.

Creating a console application with ‘custom’ HTTP call to the API is way more difficult than using the PnP extension. So if it is possible and allowed by your internal company policy, PnP is definitely the way to go.

Extra references:

Install & Uninstall SharePoint apps by code announced at ESPC 2017

Today at ESPC 2017, Vesa Juvonen announced the availability of an endpoint on SharePoint Online to install, uninstall, deploy, retract, upgrade, and remove SharePoint apps programatically.
As I was thrilled by the announcement, I immedialty tested it out on my development environment with this blogpost as result (for the items I could test by myself).

So, wich information do we get from the endpoint, which actions can we execute?

Get apps

First of all, the endpoint itself looks like: _api/web/tenantcatalog
Using that endpoint (GET) will provide the following result:

The bold ID is the identifier which we have to you to execute the other actions like install, uninstall…

Other properties are also returned like:

  • AppCatalogVersion: showing the version of you app in the appcatalog (so not the installed version).
  • CanUpgrade: true or false, depending if you can upgrade you app to the latest version.
  • Deployed: if the app is deployed on the current site for where you are executing the call.
  • InstalleVersion: the current installed version of the app, can be lower than the appcatalogversion.
  • IsClientSideSlution: if the application is a SPFx solution.
  • Title: the title of the application.


POST actions

The following actions gives you the posibility to perform some usefull actions with your app and your app package at sites and at the app catalog.
As you can notice, the add-command isn’t described below. That’s for the simple reason I can’t get it working. For that call I have to wait for the official documentation of Microsoft, so it is clear which body has to provided to add an app to the app catalog.

Update (18/11/2017): The official documentation is out and can be found (together with the PNP Powershell cmdlets) at: Application Lifecycle Management (ALM) APIs

Install app

To install an app, you have to use the following endpoint: _api/web/tenantappcatalog/AvailableApps/GetByID({id})/Install
Where {id} has to be replaced with¬†guid’27a768cb-89fc-45ba-939c-6ac4cf617f7a’
Providing the correct X-RequestDigist header will result in an ‘200 – OK – response’, and the app will be installed

Result (200 – OK):

Uninstall app

Pretty much the same as installing an app, but instead of /install, you have to use (so obvious) /uninstall.

Result (200 – OK):

Upgrade app

If there is a newer version in the appcatalog available for your specific app, you can use this action to upgrade to app to the latest version (and again the same as with install and uninstall):

Result (200 – OK):

Remove, retract, deploy

For remove, retract,  and deploy you have to go to the app catalog site collection (there were the app packaged where added).
First you have to retract the app from your tenant by the following action/command:

To redeploy the app to your tenant you can use

To entirely remove the app from the app catalog you can use the following action/command:


Start your MS Flow from within the new SharePoint column formatter

At Ignite, Microsoft announced the new column formatter for SharePoint online. It provides the flexibility to format your columns based on your data.

SharePoint list with three columns formatted

Lincoln DeMaris (BRK3252) explained how the formatting worked with a lot of useful demos. One of them was a button/link in a column that launches an MS Flow for that specific item in your list. Unfortunately, the article does not provides the JSON yet, but you can copy paste it from here. The only thing you have to do is to create an MS flow, copy paste the GUID, and give it a relevant name in the JSON. If you want to format the column, you can take a look at the documentation of Microsoft on: Get started with column formatting (

Important note: The flow has to be able to start on it’s own. This means that the trigger for the flow has to be for example the “For the selected item”-trigger of the SharePoint repository. Otherwise the panel on the right won’t show up and the console logging will show you that the flow can’t be executed.

And example of a working flow:

Extra information:

Use column formatting to customize SharePoint (

Geek out with the product team on SharePoint lists and libraries (Youtube)

How to activate modern pages on a classic team site

Recently I was searching for how to get the new Modern Pages in an old classic team site, which was created a couple of years ago in SharePoint Online.
New modern team sites, Groups and Communication sites are already having the new Modern Pages, but the old ones are still stuck with the old fashioned pages like SP2013.
As I’m missing all the new features and eager to improve our old home page, I went looking for the trigger that defines if SharePoint creates a modern or classic page.

First I have noticed that there was an extra contenttype in the Site Pages library of Modern Team sites. Site Pages is the contenttype which is used to create the new Modern pages.

Screenshot from the Site Pages library on a Communication site



Adding this contenttype to the old Site Pages library went fine but creating a new page, based on the Site Page contenttype, results in the following error:



After comparing the activated features of the old team site and the Modern Team site I bumped into the following feature:




And as obvious as the name is, the Site Pages feature will activate the modern pages for the old team sites and let you enjoy all the new functionalities that are provided by the modern pages.

From now on, clicking on “Add a page” will create a modern page: