An awesome light-weight boilerplate to build an awesome static site, integrating Netlify CMS to be able to edit content, committing changes directly in Github and triggering new awesome live builds immediately. It's awesome!
- Portuguese Women in Tech - the very beginning of Besugo, developed first and every part of it was generalised to make way for this project and for many other websites still in development.
Below are listed the versions used in Netlify to create the live, preview and branch builds. Ideally you'd be using the exact same versions locally, although there's some wiggle room of course.
-
homebrew - a package manager for macOS, to manage applications such as
hugo
andyarn
listed below.- Install:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- If after running the above command it complains that xcode alone is not sufficient on sierra, try running
xcode-select --install
. Note that macOS Sierra 10.12.6 or higher and XCode 9.2 are required.
- Install:
-
hugo v0.53 - builds the structure of the website based on the content setup, although we're phasing it out in favor of a fully JS/React solution. Any version over 0.27.1 should still work though.
- Install:
brew install hugo
- Update:
brew update && brew upgrade hugo
- Install:
-
yarn v1.12.3 - package/dependency manager. Any version of yarn above 0.27.5 should still work though.
- Install:
brew install yarn --without-node
- Update:
brew update && brew upgrade yarn
- List installed yarn versions:
brew list yarn --versions
- Switch between yarn versions (global change):
brew switch yarn 1.12.3
- Install:
-
nvm v0.33.11 - (not used by Netlify), very useful to manage your locale Node.js versions.
-
Install or update:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
-
After installing you may need to re-open the terminal or run this for it to recognize the
nvm
command (repeat for every terminal you currently have open in which you want to use it):source ~/.bash_profile
-
-
Node.js v8.12.0 LTS (lts/carbon) - our JavaScript runtime, we need it, for everything. Any Node v8.x.x or even v6.x.x (lts/boron) should still work though.
- Install or update:
Or to immediately install the global packages from another version (including
nvm install lts/carbon
system
):nvm install lts/carbon --reinstall-packages-from=[SOME_VERSION]
- I like aliases:
nvm alias 8 lts/carbon
- Switch version in the current terminal session:
nvm use 8
- Install or update:
-
Clone project locally
-
Install dependencies:
yarn install
-
(Optional, recommended) change the port in package.json, to avoid having other Besugo projects being served on the same port, for easier window/tab management during development.
-
Serve on localhost via webpack and listen for changes (hot reloads in most cases):
yarn start
-
In your browser navigate to
http://localhost:1313
; don't forget to change the port to whatever you set in bullet point 3.
-
Rebuild all dependencies; also for any time you need to rebuild node_modules from scratch, such as when upgrading node or yarn:
yarn rebuild
(shortcut to
rm -rf node_modules & yarn install
); -
Remove leftover js files in static folder:
- static/css/cms-override.css
- static/js/site.min.js
- static/js/main.min.js
- static/js/app.min.js
- static/admin/main.min.js
-
(Resume from bullet point 3 in the Install and Run section above)
Content is of course the main focus of the website. Content types are defined in the file cms/config.yml and content entries are saved in the directory content as .md
files.
- See Using Netlify CMS below to create and edit content using the admin interface, otherwise you can just edit the files directly as code if you know what you're doing.
The folder layouts contains .html
files defining, you guessed it, the layout of that content.
-
The starting points are layouts/_default/baseof.html, defining the basic markdown of every page, and layouts/index.html, which defines the homepage.
-
Create layouts/section/[CONTENT_TYPE].html files, where
CONTENT_TYPE
matches a sub-folder inside content, for listing pages of any content type used in the website. -
Create a layouts/[CONTENT_TYPE]/single.html file, where
CONTENT_TYPE
matches a folder inside content, to structure and stylise each content entry of that type. You can create other files in this folder, to render content of that type differently as necessary for different sections of the website; e.g. a summary of a blog post to be listed in the listing page of all blog posts. -
layouts/partials contains blocks of markdown that can be reused between several other layouts; e.g. the header and footer.
-
Define the locales to be used in the website in config.yml:
Languages: en: locale: "en-GB" description: "MarzeeLab's boilerplate for the Hugo static site generator, coupled with NetlifyCMS driving Github as a backend." pt: locale: "pt-PT" description: "Boilerplate da MarzeeLabs para o gerador de sites estáticos Hugo, integrando o NetlifyCMS para uso da plataform GitHub como backend."
-
Add localized strings to be used in the website to i18n/[LOCALE].toml files.
[Home] other = "Início" [Pages] other = "Páginas" [About] other = "Sobre nós"
And use those in hugo templates:
<h1 class="home__title">{{ i18n "Home" | default "Home"}}</h1>
-
Content entries need to be input already in the desired locale of course. Just define separate content types for each localization that is needed; see Using Netlify CMS below on where and how to do this:
-
Set the content type's
name
with a "-[LOCALE]" suffix:name: `blog_post-pt`
-
Make sure its
folder
is the same across locales for the same type of content. -
Set its slug to include the locale as well, to differentiate the
.md
files for content with the same name:slug: "{{title}}.{{i18nlanguage}}"
-
Set its
filter
:filter: field: "i18nlanguage" value: "pt"
-
And finally add a hidden field to correspond to that filter, so that hugo knows what locale each content entry is written in.
fields: - label: "Content language" name: "i18nlanguage" widget: "hidden" default: "en"
-
The joy of using our boilerplate!
React components bring together the main website and Netlify CMS's interface, by using the same components to render each content type for the webpage (using server-side rendering to provide full markup on request), and live previewing content as you edit it in the CMS interface, so you can see exactly how it will look after saving.
Check out some code samples here and documentation.
-
Components go in components/SomeComponent.jsx files; a basic folder structure similar to layouts is recommended for organisational purposes, although not strictly required.
-
Every component must be initialized in the app entry point at components/App.jsx, simply import it and add it to the
initialized
array. -
Component scripts are built and processed through Babel, to allow the usage of ES6 syntax and other features in browsers that may not natively support them.
During build, the script uses sharp, a high performance Node.js image processing library, to create copies of several sizes of any images uploaded to the media_folder
(see Using Netlify CMS below). These are an integral part of using a Responsive Design approach, to optimize traffic and page load times on different devices.
We can use jpg (and the jpeg variant), png, webp and tiff file types.
To take advantage of these, you must first define in package.json what sizes and suffixes (to be appended to the original filename) sharp should work with; e.g.:
"sharp-config": {
"src": "static/media",
"dest": "public/media",
"quality": 80,
"sizes": [
{
"suffix": "-large",
"width": 1400
},
{
"suffix": "-regular",
"width": 820
},
{
"suffix": "-medium",
"width": 680
},
{
"suffix": "-small",
"width": 460
}
]
}
(Note: only change the src, dest, and quality if you also changed the media_folder value in Netlify CMS's configuration, or if you know what you're doing.)
Then to use the processed images themselves, either reference them directly in the code, or:
-
import the
SrcSet
component into any other component, or use the<SrcSet>
markup tag in the layout; -
set a src attribute, just like you would in any other
<img>
element, to the original file name; -
set a sizes attribute to tell it what sizes the image will take in the page.
The component will then build the srcset attribute automatically, and the image node will load the file that best fits each device conditions. Browsers that don't support this feature will simply ignore the srcset attribute and use the original file set in src.
Check out MDN's article on Responsive Images for more information about these attributes and how to best use them.
You can also use responsive images in backgrounds through the SrcSetBg
component. Just insert it inside the element to have the background image, configure its attributes in the same way as above for SrcSet
, and it will do its magic.
You can optionally insert SrcSetBg
anywhere in the document and set a bg-active-element attribute, with a query string pointing to the element that should have the background (id, class name, etc).
Most JavaScript dynamics should stay within the react components, since they're JavaScript already and know how to manage themselves.
-
For any extra functionality, add any
.js
files you want in js/site/ and they will be processed and concatenated into public/js/main.min.js, which you can then call from your html templates. -
Similarly, all files in js/admin/ will be concatenated into public/admin/main.min.js for use in the CMS pages.
-
Both of the above are processed through Babel similarly to the components scripts.
We use SASS to extend CSS and fulfill all our styling needs. Any .scss
in the scss directory whose name doesn't start with an _
will be compiled and minified into public/css/[PATH-TO-FILE].css.
All files to be copied as-is into the website go in the static folder.
-
For example, the admin path is simply a static/admin/index.html file, that includes the remote Netlify CMS scripts as well as our own. It needs no special processing.
-
Also ideal for images or other assets to be used in the website.
-
Typically you would set the uploads directory, defined in cms/config.yml (see more below), somewhere in this static folder, so that any assets already uploaded will remain between builds.
-
Push your Hugo project to Github
-
Choose your Github repository and branch, use build command
yarn build
and set public/ as your Public Folder. -
(Optional) to automatically deploy all branches and PRs to unique urls, in Netlify's control panel, go to settings - build & deploy - continuous deployment and set Branch deploys to Deploy all branches.
-
Set the correct repo and branch on the provided static/admin/config.yml
-
Create a new Github application following Netlify's instructions - don't forget the
https://api.netlify.com/auth/done
callback URL. -
Go to your Netlify dashboard, select your site, navigate to Access > Authentication Providers > Install Provider > Github and use the Client ID and Secret generated in step 2.
-
Start using the CMS on http://[your-website-url]/admin/
A known issue with some setups is if you type only /admin, the Netlify script will garble up the url, sending you to /admin#/ and the CMS errors. This is an issue with webpack (no such problem with live Netlify builds), and to avoid it, always include the trailing slash in the /admin/
path.
-
Edit cms/config.yml to point to your project's repo and define the content to be used in the website. You may also want to change the location of the
media_folder
andpublic_folder
depending on the upload needs/goals of each particular project. -
Every time content is edited or created, a new commit is pushed to the branch referenced, triggering a new build. In editorial workflow, Netlify CMS uses newly created branches and PRs to manage the various stages of a content entry, merging into the main branch when that entry is approved and published.
-
Use emoji country flags in content type names to display a locale selector, to filter content types only for that locale (requires 2+ different locales).
-
Avoid using special characters like
/
and#
in branch names. Even though basic usage of Netlify CMS should still work, it has a few kinks; e.g. #51.