Sometime, somehow we have wanted to put up a static website and host it somewhere quick, no frameworks, no “2017 latest web tech” to build it with. Just plain HTML and CSS; maybe some JavaScript, nothing too complex.
NOTE: In order to send messages to Pusher a server component is necessary. Github pages (or any other static hosts) will not allow you to have a real server. Therefore, to get one running, you can deploy the server that we will be creating in this article to Heroku.
It may be a single page portfolio site, listings for an event or a blog and the need for a Static Site Generator(SSG) is required. Instead of using Jekyll (built with Ruby), we choose Metalsmith because it is built with JavaScript. In this article, we are going to see how to add real-time comment system to a static Metalsmith-driven blog using Pusher. We use Pusher to render real-time comments on posts.
Here is a glimpse of what we will be building:
Simply put, metalsmith is a light-weight and extremely pluggable static site generator. When I say extremely pluggable, it just has more to do with ready made awesome plug-ins than HTML! Get that yet? Read on!
Using MetalSmith is pretty straightforward, simply create a source folder with source files in it written with markdown, metalsmith manipulates the files with the aid of plug-ins and converts the files to plain HTML in a destination folder ready to be served. Here’s an outline of how it really works:
- Create markdown files in a source folder.
- MetalSmith converts it to a JavaScript object.
- metalsmith applies set of manipulations in the form of plug-ins already supplied.
- metalsmith outputs the manipulated file as an HTML file to be served.
I assume you have Node installed as well as npm so create a project folder like metalsmith-blog, and change directory to the project folder.
https://gist.github.com/03d683bfc28af3d7e7b3bae9eea0e161
run
https://gist.github.com/70d72fa19de2d7c351b43ba012499292
These create a package.json file. We can see the metalsmith already included as a dependency in the package.json
file.
In the project folder (root), create the folders src
and build
. The src
folder would hold the source files (HTML, CSS and JS) whereas the build
folder serves as the destination for the transformed source files.
Let’s install the necessary plug-ins. It’s getting sweet!
https://gist.github.com/7a949eb12cc2a32a70440770bdb044d4
In brief:
- metalsmith: Installs Metalsmith
- metalsmith-markdown: Converts markdown files written in source to HTML
- metalsmith-templates: Enables the use of templates like Jade/Pug for layouts
- metalsmith-assets: Includes static assets like CSS and JavaScript
- metalsmith-permalinks: Customize permalink of files
- metalsmith-collections: Helps you generate grouped content list for pages like Home
- jade: preferred template engine for Metalsmith
There are a whole lot of plug-ins, thanks to the awesome metalsmith development community, you can find them here.
We got the basic folders now, and our package.json
file looks like:
https://gist.github.com/e6e78ed89a1317db485b61b75950cb3a
Note the "``scripts``"
property which was included. To serve the project on your local network, you must have serve
installed else install it with:
https://gist.github.com/b33e2f7c161053e8cf946a65d02f9b14
Assign the value of "``serve build``"
to the "``serve``"
property so whenever we run serve
in the command line, the build folder is served to our local network! Also the value "``node build.js``"
is assigned to the property "``build``"
, this ensures that when we run
https://gist.github.com/440df5a562a791ced0319817580e53fe
in the command line, the build.js
file is run.
Note: The build.js file hasn’t been created yet.
Lets get to creating the files!
Create the file build.js
in the root directory, this serves as the engine of the site, as all the plug-ins are to be configured here. It’s built as thus:
- Load the plug-ins
https://gist.github.com/25d6aeb43afcd6d9ea32be1c0d13c8b1
- Call the metalsmith function
metalsmith()
and chain all the plug-ins loaded to it with theuse()
method, passing their required arguments as well.
Take note of the order in which the plug-ins are passed as the source files will be manipulated in that order.
https://gist.github.com/0569daef08de96a0572d31ff85e18c81
Note the source and the destination directories. For this article, we used Jade as the templating tool. Feel free to try something else!
- Error Handling in case of build error
https://gist.github.com/b94fba7d4273c2585aa7c33e2fd839a2
Jade is used as the templating engine to pre-process the HTML to be written. Install Jade with:
https://gist.github.com/f60eff668ef05f3b69e6c9fe25c57fe6
Create an article.jade
file in the templates folder to server as a layout template for markdown articles:
https://gist.github.com/84094ea268a57a2e2e000d4f81e58983
Create another jade file in for the Home page layout named index.jade
in the same template directory:
https://gist.github.com/694112fc8655158b1ead691d3382e7ed
The both article have some common features — same stylesheets and head elements. The article layout displays a single article while the index layout displays a collection of articles.
In the src
directory, create an index.html
file — this is the root HTML and it is the entry point for the Metalsmith project. The content just points to the index.jade
:
https://gist.github.com/3207dde8bff1c22c67727e1a2e19a9c1
and the second:
helloworld.md https://gist.github.com/bbe1022e522607b20855fad6d871b36b
We have the front-matter on top, this contains details of the blog post and is used by article.jade
during preprocessing. The front-matter is a list of meta information about a particular document. MetalSmith picks the information up and uses it to dynamically generate a layout for each article.
Run the build command to generate output
https://gist.github.com/99857afd8bead97c66e443fd0d40020e
Swoosh! The source files in markdown have been converted to HTML files in the build
directory. Serve the build directory with:
https://gist.github.com/852764a49a14b1bb492d2d72646f249e
Now relieve the joy of seeing your static blog serving on localhost! Here is what it looks like:
Now click on one of the articles and you should see something like:
Lets get to the more fun part.
You didn’t see that coming yeah, not to worry, we wouldn’t go too deep into Vue. We only need to create the Javascript file app.js
in /src/assets/js
. In /template/article.jade
we import Vue at the bottom of the script from a CDN by putting in,
https://gist.github.com/1f214858b7e0cdfae8317d68259b7f24
The second script tag links the local JavaScript file to the jade template.
In app.js, a new Vue instance was created with a Vue component (article-comment). This component contains the template for the comment form as well as event functions to handle submission and on-screen display of comments. Check it out here.
https://gist.github.com/237680a1fce1aa940aaccd59cc9c123b
Here’s what actually happens:
- Once a comment is created and the submit button is clicked, the
onSubmit()
method is called. - The
name
andcontent
variables are updated with the name and comment in the form input. - The name:content pair is pushed into Vue’s
comment
array. - These valued are passed to the DOM via Vue
template
.
Run:
https://gist.github.com/06299cac50b61cfb5106a00f21e6d2fe
Notice that the JavaScript folder and file has been created in /build/assets
. That is how seamless metalsmith can be.
Now we can create comments and have them display on-screen!
Is this the best part yet? Hell no! Lets get to it.
Being leaders in real-time technology, Pusher gives us the power to implement real-time features in our blog with their simple hosted API.
Go on to Pusher and create an account. Create a new app and obtain an app key, this would come in very handy soon enough.
Pusher is installed in two parts, the clients-side and the server side. For the client side we install pusher and Axios by importing the script with:
https://gist.github.com/a90db6e25fd1e0126cae2da5972c9a2f
Axios enables us make HTTP requests to our local node server. There lots of alternatives to Axios but we are sticking to it because of it’s simplicity and the promise based API.
Now we have to update the /src/assets/js/app.js
file to include server configurations for the node server.
First, two new variables will be added to the data()
method and assigned a value of null
. This is to ensure that the pusher
and channelName
variable are accessible in the Vue instance.
https://gist.github.com/5398a17636546550dae82024819e26b1
We want to configure Pusher immediately Vue is loaded. To do so, we configure Pusher in Vue’s created
lifecycle method and pass the value to the pusher
property:
https://gist.github.com/2d85a1544d7dda6dfb6c386f1c6d5883
also channelName
is assigned the path of any article in view with:
https://gist.github.com/e3e08c87f7cc979b8198b0efaa76ac87
The path assigned is modified to use a dash( - ) to indicate paths instead of forward slash ( / ). This is because pusher channel names doesn’t recognize paths with strokes but with dashes.
A variable channel
with a local scope in the method is created and is assigned the result of calling the pusher.subscribe()
**method with this.channelName
as parameter.
channel
is bound to the response received from the server new-comment
, and passed a callback function with an argument comment
which is the data from the response.
This data is parsed and pushed into the comments
array on the Vue instance!
https://gist.github.com/d0bc7b374191233f18afe9b86791f7c2
Now, the method onSubmit()
still doesn’t feel right as we still can’t be handling form submissions locally without sending data over to the server right?
We need to send POST request to the server so that Pusher can emit real-time events to all connected clients.
To do so, simply send a POST
request with the form values to the server using axios:
https://gist.github.com/52075fd3d36be2ec6cf5b24dfb964f92
These dependencies are added to our package.json
file and it looks like this:
https://gist.github.com/ed2862064a2711ac9b530e9d9521e233
In our root folder, we should create a node server (server.js
) and configure it as given below:
https://gist.github.com/f80517f899c822950d4325c534691661
The build tools are first loaded, then we configure the express server using cors and bodyParser as such:
https://gist.github.com/04a38f3f05bd7cf612ebcbe572f023d4
Next up is configuring pusher using app details obtained during account creation on Pusher:
https://gist.github.com/484dac8babbe07513e702691eca82acf
Now to the most important part, configuring the express route. On receiving a POST
HTTP request for a new-comment, the request body is logged on the console,We use the trigger
method to open a Pusher channel based on the channel value on the req.body
object. The event is named new-comment
and the payload sent from the client is sent out to all connected clients via that channel. Lastly the server is created to listen on port 2000 of our localhost!
https://gist.github.com/5ce5e31e93f77d4a7f1d6a14ec3fce73
To run the server, you can update your package.json
include a run script for sever.js
:
https://gist.github.com/394421dd545d3935b2b8983f08906e6d
Execute the following to rebuild the Metalsmith app:
https://gist.github.com/be81ce3932aa489000bce2ea44016579
Then use this to start the sever: https://gist.github.com/ed796c8a5002ebc29adfcb3435ba9ba4
Here is what the realtime comments look like:
We now have our blog listening for comments. To try this out, open a separate browser, run an instance of the blog on the browser, add a new comment in the previous browser and watch it update in the new browser…in real time!
You can find the full build here.
It has surely been awesome building a static blog with metalsmith, using Vue for the comments and Pusher for the real-time updating of the comments. Feel free to add your styles to the blog. Try out other templating engines too, maybe pug. Also play around with other MetalSmith plug-ins. Have fun.