The story
You have an idea of an awesome app you want to create and you have a good knowledge of React, so what do you do?
npx create-react-app myApp
cd myApp
npm start
and you are good to go! Everything is going really smoothly in your local environment and nothing stands in your way to creating that beautiful UI, leveraging the power of React all along the way.
Time passes by, you finish your app, and surely, you want to show your app to the world.
Maybe you are not ready for a full-fledged production-ready app with a domain, and all the bells and whistles, but only want to show it to someone else. You want to have it online without too much hassle.
So, what are your options?
When running npm run build
to create an optimized production build of your app that create-react-app provides, info in the terminal window tells you this
The build folder is ready to be deployed.
You may serve it with a static server:
npm install -g serve
serve -s build
This means you need to have a server in place to get your application up and running.
The problem with servers is that computing power costs money, and there is a small chance you will find it unlimited for free anywhere, and also, you really aren't ready to pay money for your app at the moment. Other than that, setting up an HTTP server, getting a domain, etc. is a challenge on its own...
But, you are a seasoned React veteran. You know that building a React app gives you a folder with all the files you need to have an app working.
Cloud services
That is where the first major part of this puzzle gets solved... cloud computing!
But it's really not that exotic because I'm mostly talking about storage solutions those provide, Azure Blob Storage, and Amazon s3 buckets.
Both of them have an option to host a static website, and setting it up is fairly easy since both Azure and AWS provide free tier services for storage solutions that you cannot go out of if they are being used in a non-high load/traffic use-case. Definitely read the pricing tables for both of them if you plan to host something serious on them!
create-react-app on AWS s3
I will now show you a really basic create-react-app deployed to an s3 bucket, and later on, I will show you how to do it by yourself on Azure! You can easily find similar tutorials for deploying static websites on the AWS s3 bucket. Comment below if you want an AWS s3 deployment tutorial as well.
You will immediately recognize the default create-react-app screen with a twist. I added a router and an "About" page with links that can guide you back and forth.
Our demo app works great until you try to refresh, or land directly on a page which is not our home page [e.g. /about]. This is where any relatively serious app fails using this approach.
The problem is that a React app is a single-page app with just one index.html which executes js files who then do their magic and fill up our app with all the beautiful content.
If you take a look at the bucket for this app, you will quickly realize that there is no "about" folder with an index.html
file inside of it, so we rightfully get a 404 error. We would need to have a server that redirects all of our traffic to this index.html and the javascript inside which will boot up our React app and figure out what we are trying to see.
Next.js
This is where Next.js comes in and saves the day!
If you don't know what Next.js is, seriously go look it up and do a bit of research, it's awesome!
I cannot give Next.js enough justice by doing this, but I will try to sum it up for anyone who hasn't heard about it before.
Next.js is a framework for React which mainly provides server-side rendering out of the box, and it can be seen only as an "extension" to React because you still only write regular js and jsx (ts/tsx also supported!), but it is much, much more! Next.js gives us a router out of the box and it only "forces" us to use the file system as routes, so every file inside the pages
folder is a regular React component, but it is also a route.
For example, creating a component inside the pages folder like pages/about.js
instantly registers the /about
route to go to this file.
Next.js also provides us with some additional functions that help server-side data fetching which will come in handy pretty soon.
You can start a new Next.js project as easy as create-react-app.
npx create-next-app
# or
yarn create next-app
The project
I created a small Next.js app which I connected to a free cocktails API, fetched a couple of cocktails, listed them, created a detail page of every one of them. Here is the link to the project so you can check it out
https://staticappdemo.z6.web.core.windows.net/
I also provided the source code on github if you want to follow along.
I will try to explain how this is done pointing out a couple of "gotchas" inside Next.js and then do the step by step deployment to Azure!
You will notice that inside my pages
folder I have a structure like this
+----_app.js
+----index.js
+----cocktails
| +-- [id].js
| +-- index.js
pages/cocktails/index.js
is my /cocktails
route, and pages/cocktails/[id].js
is Next.js way to handle dynamic routes, so /cocktails/123abc
will go to that file and we will have 123abc
available to us inside this file as id
.
Since Next.js provides us with static HTML export, we will use that feature to fetch our cocktails in build time and deploy everything as static pages to our cloud storage solution.
The first thing we need to do is use the getStaticPaths
function that Next.js provides for us so that we can tell it what routes do we need to be generated at build time.
Inside our [id].js
file you can see this piece of code.
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get cocktails
const res = await fetch(
"https://www.thecocktaildb.com/api/json/v1/1/filter.php?a=Alcoholic"
);
const parsedRes = await res.json();
const cocktails = parsedRes.drinks.slice(0, 9);
// Get the paths we want to pre-render based on cocktails
const paths = cocktails.map((cocktail) => ({
params: { id: cocktail.idDrink },
}));
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false };
}
As you can see here, we only fetch the cocktails for their ids and map them as per documentation so that Next.js knows those are the ids we want to have for our cocktail routes.
After that, you can see getStaticProps
being used like this
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the cocktail `id`.
// If the route is like /cocktails/1, then params.id is 1
const res = await fetch(
`https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${params.id}`
);
const cocktail = await res.json();
// Pass cocktail data to the page via props
return { props: { cocktail: cocktail.drinks[0] } };
}
We use the id to fetch details of an individual cocktail and then pass it down for us to use it inside props
.
Next.js doesn't know that we want to use it as a static HTML export so it won't create the file structure as we want it for that use case. We can quickly fix that by adding this piece of code inside next.config.js
module.exports = {
trailingSlash: process.env.NEXT_PUBLIC_ENV === "prod",
};
This tells us to use the trailingSlash
when doing a production build. You now need .env.development
and .env.production
files which Next.js will automatically recognize when building the app for production, or for you to use in your local environment.
To build the app as static HTML, I added a new script to package.json
"build:static": "next build && next export"
Running npm run build:static
creates an out
folder with all our pages built inside their own id
folders. If everything went well, your out folder should look something like this:
Deploy to Azure
Creating a free Azure account should be pretty easy, and in the Azure dashboard, use the search bar on top to find the Storage accounts
service. After entering Storage accounts
you should see something like this
Click on Create storage account
button or Add
button on top left.
You will need to create a new resource group (if you haven't did that previously) and you can do that inside the wizard easily. It looks like this
Fill in the storage account name and choose a location geographically closest to you (or your audience).
Leave everything else as default and go straight for the Review + create
button.
The deployment will take about a minute or less and you should now see your new storage account inside the Storage accounts
dashboard
Click on the newly created storage account. It will open up a menu. Scroll down and find the Static website
option. Enable it and fill the Index document name with index.html
and Error document path with 404/index.html
. Next.js provides default 404 page for us. Click the save button and you will have your new website endpoint ready! It should look something like this
Scroll the menu back up to Storage explorer (preview)
click on it and open the BLOB CONTAINERS
folder and there you will see a $web
folder. That is where your built app files will be.
Before we can upload our files here, we need to add ourselves as a blob owner, otherwise, the upload will fail. To do that, on the menu, find Access Control (IAM)
option. Click on the Add role assignments
. Select the role Storage Blob Data Owner
. Assign access to User, group, or service principal
and in select
field type in your e mail address associated with your Azure account. It should look like this
The next thing you want to do is install the Azure CLI for your OS
After that is done, enter your terminal and start with logging in to Azure
az login
A new browser tab should open up for you to log in to your Azure account.
After that, you need to find out the key for your storage account. To do that run
az storage account keys list --account-name 'mladenteststorage'
just replace mladenteststorage
with your storage account name.
You should get an output that looks something like this:
az storage account keys list --account-name 'mladenteststorage'
[
{
"keyName": "key1",
"permissions": "Full",
"value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"keyName": "key2",
"permissions": "Full",
"value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
]
Take the "value"
from the "key1"
and write it down.
And finally to upload everything to our blob storage
az storage blob upload-batch -d '$web' -s 'C:\Users\mlst2502\...path-to-your-project\out' --account-name 'mladenteststorage' --account-key 'key-that-you-wrote-down-previously'
And there you have it! Your app should be visible on the URL you saw in the Static website
section of your storage account!
If you read this all the way till the end, thank you for your attention! I hope this helped you in any way, and if you have any questions about the topic, feel free to ask me in the comment section, or find me directly on any of my social networks which are listed on my website
Top comments (3)
Thanks for your article.
I got used to write my react apps with Gatsby. I can easily deploy them to Netlify for free.
Is there any reason to look into Next.js? From what I understand, it tends to do the same.
Thanks for reading through it! :)
I think Gatsby works for you, Next.js works for me, and that's all it matters.
I would like to read your take on this topic with Gatsby + Netlify. That would be really interesting
they have vercel where there is one click install available and for think its free too.