Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / Azure

Visual Studio React Application Deployment to Azure Static Web App using Azure DevOps CI/CD Pipelines

5.00/5 (1 vote)
26 Sep 2024CPOL28 min read 1.3K  
Instructions for deploying Visual Studio 2022 React projects to Azure Static Web Apps using Azure DevOps with information about project structure and how it relates to settings in the YAML pipeline.
In this tutorial I will take you through how to deploy a React application created using a Visual Studio 2022 template to an Azure Static Web App. I start with step-by-step instructions of how you first create the React application in Visual Studio and how a choice at this stage can affect the settings required in the pipeline. We then look at the structure of the project and how to find the settings that allow Visual Studio to build and run the application using Vite. Then we look at how to add your Visual Studio project to an existing Azure DevOps project. We will then go step-by-step through creating the Azure Static Web App using the Azure Portal UI and investigate the pipeline that is created in Azure DevOps. I finish by providing an alternative two stage pipeline that allows you to take control of the build process should you require it.

Introduction

Note: This article only deals with Visual Studio on Windows. It does not include instructions on installing Visual Studio, Node.js, or any other software.

If like me you tend to do all your development in the full-blown version of Visual Studio, you may find the information on the internet regarding deployment of a React web application to Azure Static Web App do not work for you. All the tutorials and Q&A entries I found were targeted at Visual Studio Code projects, and most of them used GitHub actions for deployment.

The main issue I had with the other tutorials was getting the path to the code right. Visual Studio (VS) project templates tend to wrap the project code in an additional folder layer which holds the Visual Studio solution. It is also the folder where you will create your CI/CD YAML pipeline files.

A further complication is the latest VS React projects (09/09/2024) use Vite to host the application when you debug it by pressing F5 in VS. It was not clear to me whether I needed to build the project before deploying to the Azure Static Web App. I can clear that one up right now, you do need to build the project first, but there are several ways to do that, including one pipeline step that will do both the build and the deployment.

And yet another complication is that VS allows you to change where the solution (.sln) file is in the folder structure when you create a new project based on the ‘Standalone TypeScript React Project’ template.

Quick Answer

If you tried following other posts about how to create and deploy to an Azure Static Web App but the DevOps deployment pipeline fails to build, and you don’t have time to read this full article, you probably only need the correct settings for the Build Details section of the Create Static Web App form in the Azure portal.

The Build Details section of the Azure Create Static Web App Basics form.

The required settings depend on whether you created your VS project with the solution and project in the same directory:

  • App location: /
  • Api location:
  • Output location: dist

Or the traditional way with the solution in a separate directory:

  • App location: /<project-name>
  • Api location:
  • Output location: dist

Replace <project-name> with the name of your VS project e.g. /reactproject1

Read on if you want to learn more.

The Long Answer

To create a React app in Visual Studio and deploy it to an Azure Static Web App using Azure DevOps pipelines, you need to do the following:

  1. Create your Visual Studio project.
  2. Add your project to an Azure DevOps repository.
  3. Create an Azure Static Web App.
  4. Create a CI/CD pipeline to deploy your application to the Azure Static Web App.

When creating an Azure Static Web App using the wizard in the Azure portal, steps 3 and 4 are accomplished at the same time.

The rest of this article details one way of completing this list of tasks, with some additional information to help you understand what is happening.

Create a React Project in Visual Studio

In Visual Studio you create new projects using one of the many project templates that are available. For React, you have a choice of four templates:

  • Standalone JavaScript React Project
  • Standalone TypeScript React Project
  • React and ASP.Net Core (Tag: JavaScript)
  • React and ASP.Net Core (Tag: TypeScript)

I will be using the Standalone TypeScript React Project template, but you could also use the JavaScript template if you prefer.

To create a project:

  1. Open Visual Studio in Windows.
  2. From the VS launch screen select the Create a new project option.

    Visual Studio 2022 Open Recent splash screen.
     
  3. Type React into the search box at the top of the Create a new project page.
  4. Select the Standalone TypeScript React Project template.
  5. Select the Next button.

    Visual Studio 2022 Open Recent splash screen with React search results.
     
  6. Enter a name for your project in the Project name: text box. e.g. reactproject1
  7. Accept the default Location, or if you keep your project files on a separate hard drive to your Operating System (OS), select another path.
  8. Now you have a choice to make. You can choose to create the solution file in a separate directory, or you can add the file to the project directory. The choice you make here will affect the properties required when creating the CI/CD deployment pipeline.
  9. Select the Create button to create your project.

If you select the Place solution and project in the same directory option:

Visual Studio 2022 Configure your new project form with Place solution and project in the same directory selected.

Your files and directories will look like this:

reactproject1/
├─ .vs/
├─ .vscode/
├─ public/
│   └─ vite.svg
├─ src/
│       ├─ assets/
│       │   └─ react.svg
│       ├─ app.css
│       ├─ App.tsx
│       ├─ index.css
│       ├─ main.tsx
│       └─ vite-env.d.ts
├─.gitignore
├─ CHANGELOG.md
├─ eslint.config.js
├─ Index.html
├─ nuget.config
├─ package.json
├─ reactproject1.esproj
├─ reactproject1.sln
├─ README.md
├─ tsconfig.app.json
├─ tsconfig.json
├─ tsconfig.node.json
└─ vite.config.js

If you don’t select the Place solution and project in the same directory option, which is the traditional way of organising your project:

Visual Studio 2022 Configure your new project form with Place solution and project in the same directory not selected.

Your files and directories will look like this:

reactproject1/
├─ .vs/
├─ reactproject1/
│   ├─ .vscode/
│   ├─ public/
│   │   └─ vite.svg
│   ├─ src/
│   │       ├─ assets/
│   │       │   └─ react.svg
│   │       ├─ app.css
│   │       ├─ App.tsx
│   │       ├─ index.css
│   │       ├─ main.tsx
│   │       └─ vite-env.d.ts
│   ├─.gitignore
│   ├─ CHANGELOG.md
│   ├─ eslint.config.js
│   ├─ Index.html
│   ├─ nuget.config
│   ├─ package.json
│   ├─ reactproject1.esproj
│   ├─ vite.config.js
│   ├─ README.md
│   ├─ tsconfig.app.json
│   ├─ tsconfig.json
│   ├─ tsconfig.node.json
│   └─ vite.config.js
└─ reactproject1.sln

Where Should My Solution Go?

In VS, a solution can contain multiple projects. A classic example of this is an n-tier or 3-tier application, sometimes called a full-stack application. This application would normally consist of at least one database project, API project, and web front end project (e.g. React project). When a VS solution contains multiple projects, you probably want to keep the solution at the root of the folder structure, and each project in its own sub-folder, therefore I prefer to place the solution in a separate folder to the project.

MySolution/
├─ MySolution.Database/
│   ├─ mysolution.database.sqlproj
│   └─ …
├─ MySolution.API/
│   ├─ mysolution.api.csproj
│   └─ …
├─ MySoution.WFE/
│   ├─ mysolution.wfe.esproj
│   └─ …
└─ mysolution.sln

Note: A project in VS can be included in multiple solutions. How you organise this depends on your development needs. For example, in a large project with 30, 40, or more VS projects, each project probably doesn’t reference every other project, so you don’t want all the code loaded into VS at the same time. You may group the projects into solutions that contain only the projects that are referenced by each other. In this case I would store multiple .sln solution files in the root folder; MySolution/ in the previous example folder structure.

What is Vite?

Now that you have created a new VS project, you need to understand a little bit about what you created. VS cannot host a React web application by itself. The latest React templates use Vite to host the web application on top of Node.js

So, what is Vite?

There is no point reinventing the wheel, so here is the first part of the overview from the Vite website:

Quote:

Vite (French word for "quick", pronounced /vit/, like "veet") is a build tool that aims to provide a faster and leaner development experience for modern web projects. It consists of two major parts:

(Vite | Next Generation Frontend Tooling (vitejs.dev))

Why/How does Visual Studio use Vite?

VS uses Vite as a hosting platform. When you press F5 to debug your application, VS starts Vite and Vite converts your .ts, .tsx or .jsx code, scss, etc. into standard HTML, JavaScript, and CSS that will run in your browser. It does a lot more than that, but you get the idea.

Start Debugging or F5

So, what happens when you press F5 in VS?

The answer is it depends. If you haven’t modified the settings in your VS project, VS will call npm run dev

dev is a script configured in the package.json file of your React project. If you look at that file you will see something like:

JSON
{
  …
   "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview",
  },
  …
}

To see what VS calls when you press F5, you can:

  1. Right click on the name of your project in the Solution Explorer pane and select Properties.
  2. Select Deploy then General from the navigation list.
  3. Get the command from the Startup Command text box.

Visual Studio 2022 Project Properties Deploy settings.

If you want to, you can test this by:

  1. Right click on the project name and selecting Open in Terminal.
  2. Enter the Startup Command (npm run dev) at the command prompt and press enter.

You will see the same Vite startup that normally opens in new console window.

Visual Studio 2022 running npm run dev in the Developer PowerShell.

Copy the URL next to Local: (e.g. http://localhost:5173/ in the screenshot above) in the terminal and paste it into the address bar of a new browser tab and hit return. You should see your React application.

To stop Vite:

  1. Select the Developer PowerShell terminal where Vite is running.
  2. Press Ctrl + c
  3. Type Y at the Terminate batch yob (Y/N)? message and press enter.

Visual Studio 2022 stopping the npm run dev in the Developer PowerShell.

Note: If you check the source files in your browser’s Dev Tools, you will notice that it looks like your browser is working directly with the same files that are in VS, but it isn’t. This is the power of Vite helping you to debug your source code in real time while also converting your code to HTML and JavaScript to be displayed in the browser.

Edge browser with developer tools open when debugging a React application from Visual Studio 2022.

Note: The port number that your web site runs on is configured in the launch.json file located in the .vscode directory of your project.

Visual Studio 2022 launch.json file contents for a React project.

Build Project

So, what happens when you run Build at the project level?

i.e. What happens when you right click on the project name and select Build?

The answer again is it depends. Just like debugging, you can change what VS does when it builds your project. However, the default actions for a React type project are not visible in the project properties. By default, VS will run something like npm install, which will ensure that all your project’s node modules are downloaded and up to date as specified by the dependencies and devDependencies settings in your project’s package.json file.

To see what VS calls when you start a build against your project, you can:

  1. Right click on the name of your project in the Solution Explorer pane and select Properties.
  2. Select Build then General from the navigation list.
  3. Get the command from the Build Command text box, but by default it is empty.

There is also a hidden setting in the .esproj project file that can change the behaviour of the build command. The setting is called ShouldRunBuildScript and it is a Boolean value of either true or false. The default value is false.

Default:

<ShouldRunBuildScript>false</ShouldRunBuildScript>

Visual Studio 2022 .esproj project file XML.

To see the .esproj file in VS:

  1. Right click on the name of your project in the Solution Explorer pane and select Edit Project File.

If you set ShouldRunBuildScript to true, VS will run the npm run build command after the npm install command. As with debugging, build is a script configured in the package.json file of your React project. By default the VS package.json is configured to run tsc -b && vite build.

The default build script has two commands that run one after the other:

  • tsc -b
  • vite build

tsc -b will compile/build the TypeScript in your project.

See: TypeScript: Documentation - tsc CLI Options (typescriptlang.org)

When vite build runs, Vite will convert/compile/transpile your project files into a static web application consisting of HTML, JavaScript, CSS, and supporting artifacts such as images.

See: Command Line Interface | Vite (vitejs.dev)

These generated files are saved to the directory indicated by the Build Output Folder property of the project settings.

To see the build output directory of your project, you can:

  1. Right click on the name of your project in the Solution Explorer pane and select Properties.
  2. Select Build then General from the navigation list.
  3. Get the command from the Build Output Folder text box.

By default, the Build Output Folder property is set to $(MSBuildProjectDirectory)\dist. This means that the generated files will be added to a folder called "dist" that is at the root of the project directory. If the folder does not exist, it will be created.

In the standard solution folder structure, where the solution file is not included in the project folder, this would look like this:

reactproject1/
├─ .vs/
├─ reactproject1/
│   ├─.vscode/
│   ├─ dist/
│   │       ├─ assets/
│   │       │       ├─ index-<hash>.css
│   │       │       ├─ index-<hash>.js
│   │       │       └─ react-<hash>.svg
│   │       ├─ index.html
│   │       └─ vite.svg
│   ├─ public/
│   ├─ src/
│   ├─.gitignore
│   └─ …
└─ reactproject1.sln

The exact number and names of the files will depend on how you develop your website. For example, if you add localisation to your website and store your translations in additional .json files, these files and their folder structure will be added to the "dist" directory so long as they are added to or below the public or src folders.

Note: If your project is connected to an Azure DevOps or Git repository, by default the "dist" folder is not (and shouldn’t be) uploaded to the repository. Each developer should create their own set of dist files as and when they need them, as often as they need them. The setting for this is found in the .gitignore file in the root folder of the project. If your project does not have a .gitignore file, you can add one yourself. The file name is just .gitignore, there is not prefix or additional file type suffix.

The default entries for the .gitignore file in a VS React project is:

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

Preview

You may have noticed that there is a script in the packages.json file called "preview". There is no command in VS that calls this script, but you can run it yourself using npm run preview. You must run npm run build before you run the preview command.

The preview command will use Vite to host your application using the files in the "dist" folder. Therefore, it is a reproduction of what your application will look and behave like when it is running in production.

By default, the preview site is hosted on port 4173. There isn’t a setting in the default VS template files for this port number.

Visual Studio 2022 npm run preview running in Developer PowerShell.

As with running the npm run dev command detailed above, copy the URL next to Local: (e.g. http://localhost:4173/ in the screenshot above) in the terminal and paste it into a new browser tab and hit return. You should see your React application.

The preview port number may be set in the packages.json file by amending the preview script, but there appears to be a firewall or other network setting that prevents you accessing the site. I haven’t delved into this to work out where the issue is yet. I suspect this may be Windows firewall settings. If that is the case the behaviour may be different for you, depending on the firewall rules you have set. Setting port 80 or 443 work.

"preview": "vite preview –port 80",

Although I haven’t tried it, apparently you can set the preview port in the vite.config.ts file with something like:

JSON
export default defineConfig({
  preview: {
    port: 8080
  }
})

See: reactjs - How to set vite (preview) production port? - Stack Overflow

Lint

The final script is "lint". This doesn’t affect the deployment of your application to Azure Static Web App, but I mention it here for completeness. ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. Running the command npm run lint will cause VS to check your code for potential errors and code style. You don’t need to run this because VS continually runs the linting for you as you make changes. This is what puts the squiggly lines under your code in the React project. What linting checks for is highly configurable and a huge subject by itself.

Add the React Project to Azure DevOps Repository

I’m not going into how to set up an Azure DevOps project or repository because they are outside the scope of this blog. However, I do think it is worth noting how you can easily add your VS solution to an existing repository using the VS user interface.

The first thing you need to do is to sign into VS with an account that has permissions to the Azure DevOps project and repository where you want to add your solution.

  1. Select the Sign in button from the top right of VS.

    Visual Studio 2022 Sign In button location.
     
  2. In the Sign in dialog box, select the appropriate account then select the Continue button.

    Visual Studio 2022 choose accont to sign in with dialog box.
     
  3. (Optional) If like me your account has access to multiple Azure tenants (customers, dev, B2C, etc.), you may want to deselect the tenants that you do not develop against. This helps to limit the number of options you are presented with when asking VS to work with Azure related resources, such as deploying to Azure or selecting Azure DevOps organisations.

    You do this by:
    1. Select the icon that replaced the Sign in button from step 1.
    2. Select the Account settings link below your account picture.

      Visual Studio 2022 location of Account Settings.
       
    3. In the account settings dialog box, select the Apply filter… link next to the account that you just signed in with.

      Visual Studio 2022 Personalization Account form showing the location of the Apply filter link.
       
    4. In the Filter account dialog box, deselect the tenants that you do not want to develop for from the list of available tenants, then select the Apply button.

      Visual Studio 2022 Filter account dialog box with tick boxes to select Azure tenants.
       
    5. Select the Close button on the account settings dialog box.
  4. Select the Add to Source Control button at the bottom right of VS, then select Git.

    Visual Studio 2022 showing the location of Add to Source Control button.
     
  5. In the Create a Git repository dialog box:
    1. Select the Azure DevOps option.
    2. Ensure that Default (VisualStudio) is selected as the .gitignore template.
    3. (Optional) Choose a License template.
    4. (Optional) Select the Add a README.md option. This will add a read-me markdown file to the root of your new repository. If you have added the solution into the same directory as the project, do not select this option. There is already a README.md file in this location.
    5. Ensure that the correct Account is selected. This should default to the account you signed in with.
    6. Select the Azure DevOps Organisation from the dropdown list. If your organisation is not listed, check:
      1. You are logged in with the correct account.
      2. The correct account is selected.
      3. Your account has the required permissions to the Azure DevOps organisation.
    7. Select the Azure DevOps Project from the dropdown list.
    8. Enter a Repository name or accept the default name.
    9. Select the Create and Push button.

      Visual Studio 2022 form for adding a project to an Azure DevOps code repository.
       
  6. Give Visual Studio a moment to action your changes and push your code to the new repository.

Visual Studio 2022 showing a project connected to an Azure DevOps code repository.

Note: This method results in the first branch being called master. If you like your primary branch to be called main, you will need to close VS, navigate to your Azure DevOps instance in a browser, create a new branch, delete the old branch, reopen VS, then select the new branch from the bottom left (where it says master in the picture above). In short, it’s a bit of a mess-around.

Hosting in Production

In production you will not be running your application on top of Vite. This is where the "dist" folder and its contents created by npm run build come in. You cannot simply navigate to the "dist" folder and double click on index.html to run your application. You need to host the files from a proper web server.

Test Production Code Without Vite

I’m going to go a little old-school for this test, but it’s what I know. If you have IIS (Internet Information Services) installed, you can test running the production files of your application without vite.

  1. Right click on the name of the "dist" folder below your project in the Solution Explorer pane and select Open Folder in File Explorer.
  2. Copy the full path to the folder from the Windows Explorer address bar.

    It should be something like:
    C:\Users\<username>\source\repos\ReactProject1\ReactProject1\dist
     
  3. Open IIS manager:
    1. Press Windows Key + R
    2. Type inetmgr.exe in the Open: text box.
    3. Select the OK button.
  4. In the Connections pane on the left, expand the server (machine name) node and the Sites node.
  5. Right click on the Sites node and select Add Website.
  6. Enter the following details in the Add Website dialog box:
    1. Site name: Any suitable name for your application.
    2. Physical path: The path copied in step 2.
    3. Port: A port number that is not being used by any other web site or service.
      If this is the only web site you are hosting, use 80, otherwise try 8080 or 6173.
  7. Select the OK button to create the site.

    Internet Information Services (IIS) Add Website form with completed settings.
     
  8. Select the name of your new site from the Connections pane on the left.
  9. Select the Browse *.<port> (http) link below Browse Website in the right-hand pane.

    Internet Information Services (IIS) Manager showing the location of the Brows Website link.

All things being well, you should see your application pop up in your default browser.

React Website created in Visual Studio 2022 running from compiled code in IIS.

Note: These instructions are the very minimum requirements to test your web application. IIS website hosting is another huge topic that is not relevant to this post.

This test demonstrates what is needed to run your React application. At this point, none of the other files outside the "dist" folder that make up your VS project are being used. Azure Static Web App will replace IIS in production.

Create an Azure Static Web App

These instructions will show you how to create an Azure Static Web App using the Azure Portal user interface.

  1. Open the Azure Portal in a browser (https://portal.azure.com/)
  2. Make sure you log in with an account that has access to the Azure DevOps project where your React app repository is located. Your account will also need the appropriate permissions in the Azure DevOps project to add files to the repository and create Azure DevOps Library entries.
  3. Select the + Create a resource button from the top of the home page.

    Azure Portal Create a resource button location.
     
  4. Type azure static web app into the Search services and marketplace search box and hit return.
  5. Select Create then Static Web App from the Static Web App service results.

    Azure Portal Create a Static Web App tile showing the location of the link to jump straight to the form.
     
  6. On the Basics tab of the Create Static Web App form:
    1. Ensure that the correct Subscription is selected.
    2. Select Create new below the Resource Group dropdown list.
    3. Enter a name for the new resource group. e.g. mc-reactproject1-rg
      Note: I tend to use <my-initials>-<projectname>-rg for resource group names
    4. Select the OK button to close the new resource group popup.
    5. Enter a Name for your Static Web App. e.g. ReactProject1
    6. Select the appropriate Plan type. Free is fine for a demo or playing around with. Typically, you will want Standard unless you have specific needs for the new Dedicated plan.
    7. Select Azure DevOps from the Source list.
    8. Select the Azure DevOps Organisation.
    9. Select the Azure DevOps Project.
    10. Select the Azure DevOps Repository.
    11. Select the Azure DevOps Branch.
    12. Select React from the list of Build Presets.
    13. If your solution file and project file are in different folders, enter /<project-name> in the App location text box.
      If your solution file and project are in the same folder, enter / int the App location text box.
    14. Leave the Api location blank since we are not consuming any external APIs.
    15. Enter dist in the Output location text box.
    16. Select the Next: Advanced > button.

      Azure Portal Create Static Web App Basics tab form filled in.
       
  7. On the Advanced tab of the Create Static Web App form:
    1. Select the region that is closest to you.
    2. Select the Next: Tags > button.

      Azure Portal Create Static Web App Advanced tab form filled in.
       
  8. On the Tags tab of the Create Static Web App form:
    1. Enter the name of any tags you normally create. e.g. Environment
    2. Enter the value of the tag. e.g. Development
    3. Repeat steps a and b for all required tags.
    4. Select the Next: Review + create > button.

      Azure Portal Create Static Web App Tags tab form filled in.
       
  9. On the Review + create tab of the Create Static Web App form:
    1. Check the details displayed on the page.
    2. Select the Create button to start the process of creating the resources.
  10. You will be redirected to the deployment page for your new resources. If everything goes well, a Go to resource button will appear. Select the Go to resource button.
  11. You should be presented with the Overview page for your new Static Web App.
  12. Select the URL link to view the default contents of the new web site.

    Azure Portal Static Web App Overview page showing the location of the URL.

You should see your React app displayed in the browser, and if you select the count button in the middle, you should see the count increment.

If you do not see your React app straight away, it is probably because your new CI/CD pipeline in Azure DevOps has not finished running yet. Wait a few minutes (normally less than 2 minutes) then refresh the browser tab.

Edge browser showing Visual Studio 2022 React application running from an Azure Static Web App.

Investigating the CI/CD Pipeline

The Create Static Web App wizard in the Azure portal has created a YAML pipeline file for you and added it to the root of you Azure DevOps project that you specified.

The pipeline file will be called something like:

azure-static-web-apps-<random>-<random>-<random>.yml

It will contain script like this:

YAML
name: Azure Static Web Apps CI/CD

pr:
  branches:
    include:
      - master
trigger:
  branches:
    include:
      - master

jobs:
- job: build_and_deploy_job
  displayName: Build and Deploy Job
  condition: or(eq(variables['Build.Reason'], 'Manual'),or(eq(variables['Build.Reason'], 'PullRequest'),eq(variables['Build.Reason'], 'IndividualCI')))
  pool:
    vmImage: ubuntu-latest
  variables:
  - group: Azure-Static-Web-Apps-ambitious-bush-02470c603-variable-group
  steps:
  - checkout: self
    submodules: true
  - task: AzureStaticWebApp@0
    inputs:
      azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_AMBITIOUS_BUSH_02470C603)
###### Repository/Build Configurations These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
      app_location: "/reactproject1" # App source code path
      api_location: "" # Api source code path - optional
      output_location: "dist" # Built app content directory - optional
###### End of Repository/Build Configurations ######

In brief:

  • The display name for the pipeline is Azure Static Web Apps CI/CD.
  • It has a pull request trigger looking at the master branch of your repository.
  • It has a push trigger looking at the master branch.
  • It has a single stage (implied) with a single job with a display name of Build and Deploy Job.
  • The pipeline runs on the latest Ubuntu Linux build agent virtual machine provided by Microsoft.
  • It uses variables in a group called azure-static-web-apps-<random>-<random>-<random>-variable-group which is stored in Pipelines > Library > Variable groups.
    • The group has one variable which is the deployment token from your Azure Static Web App.
  • The job performs two actions in sequence, checkout, then a AzureStaticWebApp@0 task, if the Build.Reason variable is one of the following:
    • Manual
    • PullRequest
    • IndividualCI
  • The checkout action will take the latest files from your main branch and add them to the working directory of the build agent.
  • The AzureStaticWebApp@0 task will:
    • Start in the directory indicated by app_location. This is relative to the working directory.
    • It will process the file in the working directory using Oryx

      “Oryx is a build system which automatically compiles source code repos into runnable artifacts. It is used to build web apps for Azure App Service and other platforms.”
      (microsoft/Oryx: Build your repo automatically. (github.com)).

      It runs npm install followed by npm run build.
    • Oryx will validate that the build files are in the output_location folder.
    • Oryx will not try to do anything with an API because api_location is set to “”.
    • It will create a zip file with the files in the output_location folder and deploy them to your Azure Static Web App using the deployment token from the variables group.

Note: Build.Reason is a predefined pipeline variable (Predefined variables - Azure Pipelines | Microsoft Learn). It holds a value that indicates the event that caused the build pipeline to run.

See Build configuration for Azure Static Web Apps | Microsoft Learn for more information about the AzureStaticWebApp@0 task.

The variable group that was created by the Create Static Web App wizard contains a single variable called AZURE_STATIC_WEB_APPS_API_TOKEN_<random>_<random>_<random>.

The variable is protected so you cannot view its value in the library:

  1. Open Azure DevOps in a browser.
  2. Select Pipelines then Library from the left-hand menu.
  3. Choose the required variable group from the list of variable groups.

Azure DevOps Library page showing some variable groups created by the Create Static Web App wizard.

Azure DevOps Library variable group showing the value of the deployment token cannot be read.

If for some reason access between the Azure DevOps pipeline and your Azure Static Web App starts to fail because of authentication, you may need to replace this value.

Even though the variable calls this value the Azure Static Web App API token, the Microsoft documentation and Azure UI call it the deployment token. To get the current deployment token or generate a new token:

  1. Open the Azure Portal.
  2. Enter Static Web App in the search box at the top-centre of the home page and select the Static Web Apps below the Services heading.
  3. Select your web app from the list of available web apps.
  4. Select the Manage deployment token from the top menu bar of the Overview page.

    Azure Portal Static Web App Overview page showing the location of the Manage deployment token button.
     
  5. In the Manage deployment token panel that appears on the right:
    1. (Optional) Select the Reset token button to generate a new token.
    2. Select the copy icon to copy the existing token.

Microsoft details the process here: Reset deployment tokens in Azure Static Web Apps | Microsoft Learn

Create Your Own CI/CD Pipeline

There are times when you may not want Oryx to build your project, for example:

  • Oryx may not be able to build your project.
  • You may want to do additional testing between building and deploying.
  • You may want to generate a package of the deployed files for use elsewhere in the pipeline.

In these cases, we can build our own YAML pipeline based on what we have learned from the generated pipeline.

YAML
name: Azure Static Web Apps Custom CI/CD

pr:
  branches:
    include:
      - main
trigger:
  branches:
    include:
      - main

stages:
- stage: Build # The first stage runs on an Ubuntu Linux build agent provided by Microsoft
  displayName: Build stage
  jobs:
  - job: Build
    displayName: Build
    condition: or(eq(variables['Build.Reason'], 'Manual'),or(eq(variables['Build.Reason'],'PullRequest'),eq(variables['Build.Reason'], 'IndividualCI')))
    pool:
      vmImage: 'ubuntu-latest'

    steps:
    - task: NodeTool@0 # Ensure Node.js is available on the agent
      inputs:
        versionSpec: '20.x' # Use Node.js version 20.x (or adjust to your preferred version)
      displayName: 'Install Node.js'

  # Run commands to restore node.js modules and build
  # Copy the dist directory to the staging directory
    - script:
        echo "##### Current working directory: $PWD"
        echo '##### npm install:'
        npm install 

        echo "##### List node_modules dir contents:"
        ls node_modules

        echo '##### npm run build --if-present:'
        npm run build --if-present

        echo "##### List working dir contents:"
        ls

        echo "##### List dist dir contents:"
        ls dist

        echo '##### npm run test --if-present:'
        npm run test --if-present

        echo "##### copy the $PWD/dist directory to staging: $(Build.ArtifactStagingDirectory)"
        cp -r $PWD/dist $(Build.ArtifactStagingDirectory)

        echo "##### List $(Build.ArtifactStagingDirectory)/dist dir contents:"
        ls $(Build.ArtifactStagingDirectory)/dist

      displayName: 'Build Vite React App'
      workingDirectory: '$(System.DefaultWorkingDirectory)/reactproject1'

    - task: PublishBuildArtifacts@1 # Package everything in the staging directory into a package called drop
      displayName: 'Publish Build Artifacts'
      inputs:
       pathtoPublish: '$(Build.ArtifactStagingDirectory)'
       artifactName: 'drop'

- stage: Deploy # The second stage will run on another build agent
  displayName: Deploy stage
  dependsOn: Build
  condition: succeeded()
  jobs:
  - job: Deploy
    displayName: Deploy
    condition: succeeded()
    pool:
      vmImage: ubuntu-latest
    variables:
    - group: React-i18next-Library


    steps:
    - task: DownloadPipelineArtifact@2 # Download the drop package created in the previous stage
      displayName: 'Download Build Artifacts'
      inputs:
        artifact: drop
        downloadPath: $(System.ArtifactsDirectory)
       
    - task: AzureStaticWebApp@0 # Deploy the dist directory to Azure Static Web App without building
      inputs:
        workingDirectory: $(System.ArtifactsDirectory)
        azure_static_web_apps_api_token: $(StaticWebAppToken)
        skip_app_build: true
        app_location: "dist"
        api_location: ""
        output_location: ""

This pipeline has two stages. When using the Microsoft provided build agents (vmImage: 'ubuntu-latest'), the two stages will run on different virtual machines, i.e. a new virtual machine is spun-up for each stage. Because of this, we need to add any files that we wish to share between the build agents to a published package.

The first stage is responsible for building the distribution files from our project. The repository files are added to the working directory of the build agent by default; I have not added a step to do this.

The first step ensures that node.js is added to the build agent (NodeTool@0).

Next, we run some Linux bash commands npm install and npm run build to build compile the production files for our project (script). Notice that the workingDirectory is set to the project name, this is because I am not storing the solution file in the project directory. If your solution and project files are in the same folder, you would use workingDirectory: '$(System.DefaultWorkingDirectory).

After the build command, script copies the dist folder to the artifact staging directory ($(System.ArtifactsDirectory)).

I have also added in a bunch of directory listing commands to script so that the contents of the directory are displayed in the Azure DevOps pipeline logs. I find this a useful technique for debugging YAML pipelines.

Note: If you want to run the pipeline on a Windows build agent, these commands should still work but remember that the script will be running PowerShell commands.

After the script has completed, we run a step to compress any files waiting in the artifact staging directory into a package called drop, we then publish that package to Azure DevOps (PublishBuildArtifacts@1).

The second stage is responsible for deploying the production files to the Azure Static Web App. Because this is a new stage, you may have to wait for a little while for a build job slot to become available and for the build agent to be created. How long you wait will depend on how many concurrent jobs your Azure DevOps environment is configured to use.

The first thing this stage does is to download the package called drop that we created in the first stage and unpack it to the artifacts’ directory (DownloadPipelineArtifact@2).

The final step is to run the AzureStaticWebApp@0 task with the correct settings to publish files to the Azure Static Web App without running a build. See Build configuration for Azure Static Web Apps | Microsoft Learn

Note: You can download packages to your computer after a pipeline has finished. In Azure DevOps, select the name of the pipeline, then select the instance/run of the pipeline that you want to get the package for. You will find the package in the middle of the heading data under Related. Select the n published link and you will be able to explorer the contents of the package and download anything that you need.

Azure DevOps pipeline run results page showing the location of the link to view the published package contents.

Conclusion

If you made it this far, thanks for sticking with me. Once you understand what React in Visual Studio is doing, and what the Create Static Web App wizard creates, it’s actually very simple to work out what values should choose for the application and output file locations.

I know I have covered a lot of topics in this article, but I wanted to ensure that this article would stand alone as a single point to starting your journey towards deploying React applications from Visual Studio to Azure Static Web Apps using Azure DevOps. I think there is nothing more frustrating than finding an article that appears to answer your questions, only to find that half of the information is in other articles that are no longer available.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)