Rate this page:

Using a Front-End Framework with Twilio Serverless

Additionally to using Twilio Assets to host audio files for your phone calls or files for shared logic between your Twilio Functions you can also use it to serve front-end web assets such as HTML, JS and CSS files.

In this guide we'll talk about how you can use the Serverless Toolkit to use front-end frameworks to build you web assets. As an example we'll be using React as a front-end framework but you can use any common framework including Vue and Angular.


Setup your Twilio Serverless project

If you don’t already have an existing project using the Serverless Toolkit, go ahead and create one by following the instructions in our Getting Started guide. Otherwise, keep reading.

Pick your build tool

In this guide we'll be using Parcel as our build tool of choice but you can apply similar steps to Webpack or other bundlers.

Make sure to install your bundler tool in the devDependencies section of your package.json.

To install Parcel run:

npm install parcel-bundler --save-dev

Define your project structure

By default a project created with the Serverless Toolkit will contain a functions/ and an assets/ directory. In our case we'll want to keep the two directories but we'll also add two new directories:

  • src/ will contain our front-end code that we'll end up passing through the Parcel Bundler
  • dist/ will serve as the source of truth for the assets that we'll deploy. In our case this will be a combination of the bundled assets from src/ and the static files from the assets/ directory.

Create the src/ directory and add an index.html file to it as this will serve as the entry point for our bundler.

This is where you'll reference other files that should be bundled such as stylesheets or JavaScript files. For more information, make sure to check out the Parcel documentation.

Update your gitignore

Since the dist/ directory contains bundled files it does not make sense to version control this directory. Instead make sure to add it to your .gitignore file at the root of your project.


Install other helpful tools

We'll be using two other tools:

  • concurrently to run the Parcel build process at the same time as twilio-run to serve our project.
  • ncp to copy files from assets/ to dist/
  • rimraf to delete the dist/ directory before building the Assets

Install the two dependencies by running:

npm install concurrently ncp --save-dev

Configure the Serverless Toolkit

By default the Serverless Toolkit will use the assets/ directory to serve and deploy Twilio Assets.

You can modify this behavior by using the --assets-folder flag but the easier way is by creating/modifying your .twilioserverlessrc file at the root of your project the following way:

    "commands": {},
    "environments": {},
    "projects": {},
    // "assets": true   /* Upload assets. Can be turned off with --no-assets */,
    "assetsFolder": "dist"  /* Specific folder name to be used for static assets */,
    // ..

Set up your build steps

Next we need to define a couple of "scripts" inside our package.json to make executing the right steps easier.

Modify the scripts section of your package.json the following way:

  "scripts": {
    "prebuild": "rimraf dist"
    "build": "parcel build src/index.html -d dist",
    "postbuild": "ncp assets dist",
    "predeploy": "npm run build",
    "deploy": "twilio-run deploy",
    "start:web": "parcel watch src/index.html -d dist",
    "start:twilio": "twilio-run",
    "prestart": "ncp assets dist",
    "start": "concurrently npm:start:web npm:start:twilio"

This way you'll have three distinct commands:

  • npm start will start the build server for Parcel and serve all Assets and Functions locally using twilio-run
  • npm run build to build all front-end assets and copy over all static files from assets/
  • npm run deploy will run npm run build and take all files in dist/ and deploy them together with all Functions from functions/ to Twilio Functions.

If you are using `twilio serverless:deploy` to deploy your application instead of `twilio-run` you have to manually run `npm run build` first and then can normally deploy using `twilio serverless:deploy`.

Start building your front-end

Now that we set up our build chain you can start developing your project.

Most things should work as you are used to but there are a few things to keep in mind.

Installing Dependencies

All dependencies for your UI should be installed as devDependencies unless they are also used from within your Functions. For example, let's say we want to use React with Twilio's Paste Design System. We'd install the dependencies the following way:

npm install react react-dom prop-types @twilio-paste/core @twilio-paste/icons --save-dev

Note the --save-dev at the end. If you are using yarn or pnpm both of them support equivalent flags to store dependencies as devDependencies.

If you do not install them as devDependencies but as dependencies instead, they'll be installed in your Twilio Functions deployment which increased deployment times and might cause other unintended side effects including failed builds.

Multiple file versions

When you run npm run deploy or twilio serverless:deploy the tool will automatically deploy everything in the dist/ folder but nothing else. Meaning if you are building your output files with file hashes in the name (e.g. main.a124bfc.js) and the build changes that hash to a different one (e.g. main.a12222f.js) and you deleted the dist/ folder prior to building (default in the scripts above) this will result in main.a124bfc.js to not be served anymore post deployment. The Serverless Toolkit currently does not support additive deployments that would support both versions to be available.

On the flip-side keeping your dist/ folder as lean as possible will reduce the deployment time.

Calling Functions

With the setup described above you should be able to call any of your Functions that exist in the functions/ directory by specifying the absolute path of them. For example for a Function with the file functions/token.js you can make an HTTP request from within your front-end to /token.

For example:

  .then(response => response.json())
  .then(data => {

Static Assets

We talked about "static" assets above? What are those and why are we keeping those separately in a assets/. With static assets in this case we mean any file we don't want to have processed by our build tooling. Common types of such assets could be media files — for example an mp3 file that you are referencing in the TwiML for your Twilio Voice call — or a private JavaScript module that you are referencing for from one of your Twilio Functions to store shared logic. In general private assets are likely going to be static assets that you might want to store in the assets/ directory.


At the current moment Twilio Assets does not support the following two features that you might have to consider when building your front-end on Twilio Serverless:

  1. No URL fallbacks/redirects meaning you cannot use tools such as react-router unless you are operating them in a hash-based routing mechanism. You'll always have to serve to specify the full URL. The only current exception is if you have a file with the path /assets/index.html you can use / to reference the same file. In our case this means that you'd have to have Parcel output a file in the directory dist/assets/index.html which then, once deployed, will be available both at https://<service_name>-<number>-<environment_suffix> and https://<service_name>-<number>-<environment_suffix>
  2. No built-in build system you have to build all the respective files and trigger your deployment either locally or using your own CI/CD system of choice. Check out the following guide on using Twilio Serverless from CI/CD for more guidance.
Rate this page:

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd by visiting Twilio's Stack Overflow Collective or browsing the Twilio tag on Stack Overflow.

Loading Code Sample...

        Thank you for your feedback!

        Please select the reason(s) for your feedback. The additional information you provide helps us improve our documentation:

        Sending your feedback...
        🎉 Thank you for your feedback!
        Something went wrong. Please try again.

        Thanks for your feedback!