Let's see what it takes to set up a Gatsby blog inside a Phoenix app!
Like Jekyll, VuePress and other popular static site generators, Gatsby is a easy way to create a snappy blog. Since it generates plain HTML, CSS and JS (aka "a static site"), all that's really needed is to drop the files in a directory that serves static contents.
By default, Phoenix includes a directory for serving static assets at
/assets/static. Any files and subdirectories named in the
only: key of
Plug.Static inside your
endpoint.ex file will be loaded from this static assets directory instead of being delegated to your router.
- Make sure you've installed Node, Elixir and Phoenix
- Install the Gatsby CLI:
npm install -g gatsby-cli
- Create a new Phoenix app:
mix phx.new my_app
- Go into the newly created directory:
- Generate the DB:
- Go into the assets directory (of your new app):
- Create a Gatsby site inside
gatsby new gatsby https://github.com/gatsbyjs/gatsby-starter-hello-world
Verify the installations were successful
At this point, you should be able to start your Phoenix app by running
mix phx.server from the base of your project, the
my_app directory and opening
localhost:4000 in your web browser.
The static site generated by Gatsby can be run in development mode by running
gatsby develop from
my_app/assets/gatsby and then opening
localhost:8000. It's just a "Hello world!" for now.
We named the site "gatsby" in the previous section so that it's clear, from the context of the enclosing Phoenix project, that this is a Gatsby site.
Generate the static site
In order to run the static site in production, we'll want to build a production bundle. The command to do it is
gatsby build. By default this will build the site under a directory called "public", which in our case will be at
This isn't ideal.
We need to serve the output of the Gatsby build from our Phoenix static assets directory, but don't want to serve our Gatsby project's
src or other directories publicly. There is an
--output-dir flag that can be passed to the
gatsby build command, but at the time of this writing, it's not possible to pass an output directory outside the Gatsby project, only the name of the directory is configurable.
Create a symlink
One way to get around this issue is to create a symlink from Gatsby's output directory to the static assets directory. First cd into the
/my_app/assets/static directory, and then...
ln -s ../gatsby/public blog
mklink ..\gatsby\public blog
Now, the entire contents of whatever is generated in Gatsby's public output directory will be visible as a "blog" directory under the Phoenix static assets directory.
Enable the blog directory
The final step is to open up
endpoint.ex and update the
Plug.Static portion near the top to allow it to handle the newly created blog directory (which is a symbolic link of the Gatsby public directory). It should look like this:
defmodule MyAppWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :demo
socket "/socket", MyAppWeb.UserSocket,
plug Plug.Static.IndexHtml, at: "/blog"
only: ~w(blog css fonts images js favicon.ico robots.txt)
# The rest of the file
After saving these changes, and restarting the Phoenix app, the generated Gatsby site is served from within the Phoenix app at
Possible next steps
Now that everything is up and working, You could just build out the web app and static blog. For many apps and sites, no further integration is necessary.
Here are some ideas, though...
See if you can write a simple plug that will read the path from
conn.request_path and redirect
/blog.index.html. Or alternatively, you could look at a library
plug_static_index_html and see if you can use it to handle this for you.
Try installing Absinthe GraphQL and serving your static Gatsby blog GraphQL from the Phoenix back-end. This is a fairly large endeavor, but it's a great learning experience!