diff --git a/_assets/images/pages/env-vars.png b/_assets/images/pages/env-vars.png deleted file mode 100644 index 74b03b36c..000000000 Binary files a/_assets/images/pages/env-vars.png and /dev/null differ diff --git a/_assets/images/pages/env_var.png b/_assets/images/pages/env_var.png new file mode 100644 index 000000000..a37da5073 Binary files /dev/null and b/_assets/images/pages/env_var.png differ diff --git a/_kbarticlespages/single-page-application.md b/_kbarticlespages/single-page-application.md new file mode 100644 index 000000000..cdcdcdf14 --- /dev/null +++ b/_kbarticlespages/single-page-application.md @@ -0,0 +1,107 @@ +--- +layout: post +title: "Hosting Single Page Applications on Pages" +date: November 9, 2023 +excerpt: Take a look at how Pages handles single page applications +--- + + +cloud.gov Pages hosts static sites, but this doesn’t mean that developers are limited to only using [“static site generators”](https://jamstack.org/generators/): anything that can be compiled down into HTML can be run on Pages. You can use a single-page application framework like Vue.js, Svelte, Angular, React, etc. and host it seamlessly on the Pages platform. This is achieved via the `npm run pages` command which allows developers to add a custom build script, and we’ll automatically publish everything in the `_site` folder. Details on utilizing a custom build script will be covered later in the article. + +In this example we will be taking a look at a simple single-page application which uses the React library and React Router v6. You can view a repository with a full example here and see the results in Pages [here](https://federalist-01aa8660-8aca-452d-a270-5e58ffa18645.sites.pages.cloud.gov/preview/cloud-gov/pages-example-spa/content/). There are also more detailed instructions on how to run this locally in the repositories’ README.md. This article serves as a high level overview and will highlight three key features: + +* Application routing +* Environment variables +* 404 Pages + + +## Application Routing + +Static site generators like Jekyll and Hugo handle routing by creating an HTML file at each path that can be requested by the user. For single-page applications, we will only render one HTML file: index.html, which loads the JavaScript necessary for running our application and router. + +All of the routing is located in the `index.js` file. Here we import all our components, CSS and functions. We create the `` and use the `` to provide that router to our application. The parent route registers a `Layout` component which essentially wraps all the other page components thus providing a uniform structure/design for all the components. + +``` +const router = createBrowserRouter( + createRoutesFromElements( + // set route as the path plus layout + }> + }/> + }/> + }/> + }/> + + ) +); + +createRoot(document.getElementById("root")).render( + + + +); + +``` + + + +Additionally within the `Layout` component is where we house all the `NavLinks` and `navbar` which are located at the top of the page. The `Outlet` component tells the `react router` where to output the child route page components within the layout. + +``` +class Layout extends Component { + + render() { + return ( +
+

Simple SPA

+
    + {/* All nav links need to go to {path} */} +
  • Home
  • +
  • Stuff
  • +
  • Contact
  • +
+
+ +
+
+ ) + } +} + +``` + +It’s important to remember that none of these routes are creating new HTML files at a given path, they are only rendering in the browser (unless you prerender with [react-snap](https://github.com/stereobooster/react-snap)). We’ll see how to handle situations when the URL doesn’t match the HTML file in a later section. + +## Environment variables + +Pages provides [certain environment variables](https://cloud.gov/pages/documentation/env-vars-on-pages-builds/) in each build container and you can also add customer variables in your “Site Settings”. In the case of `create-react-app`, the library we’re using in this example, [we need two variables](https://create-react-app.dev/docs/advanced-configuration/): + - `BUILD_PATH`: this determines the output directory where all the built assets end up after running `react-scripts build`. In our case, Pages requires all assets to be in the `_site` folder. We can set that in the UI like so: + + + +`PUBLIC_URL`: this variable determines how assets are referenced in the final build. We can’t set this one statically in the UI because our root URL will change for preview builds. Instead, we’ll add a small bash script to our custom build command to set the default environment variable `BASEURL` as the value for `PUBLIC_URL` like this: + +``` +echo "PUBLIC_URL=$BASEURL" >> .env +``` + + You can see how this is included in the build command in the [repository](https://github.com/cloud-gov/pages-example-spa/blob/main/build.sh#L3C32-L3C32). + + + + + +## 404 Pages + +In our application code, we created a 404 page as another component to be imported into the `index.js` file to be handled by `react-router`. The Route for the 404 page was set as a wildcard catch-all so that when any path entered is not `/`, `/contact` or `/stuff` the Page404 component will be rendered. + +``` +}/> +``` + +If users navigate to a sub-path of your site directly, we need special handling for this type of 404 because the application will not have loaded your index.js file (or associated router). + +It is important to note that: + +1. Specifically for any single-page applications hosted on Pages you will need to reach out to an [Pages operator to manually set your custom 404](https://cloud.gov/pages/documentation/customization/#custom-domain-404-page) behavior to point to index.html. + +2. This will only work on your production URL, and only when you have a custom domain. For preview links, you’ll need to navigate to the base URL first before moving on to other paths.