The module cms-themeblog is the official blog theme.

Plugin dependency

Because the theme and plugins are independent EggBorn modules, the version iteration is continued.

In order to maintain code consistency during version iterations to avoid version conflicts, the theme needs to explicitly declare which plug-in modules are dependent.

By declaring the dependencies of the plugin modules, when the theme module is installed, the corresponding plugin modules is also automatically installed.


  "name": "egg-born-module-cms-themeblog",
  "version": "1.1.3",
  "title": "cms:theme:blog",
  "eggBornModule": {
    "cms": {
      "name": "blog",
      "theme": true
    "dependencies": {
      "a-instance": "1.0.0"
  "dependencies": {
    "egg-born-module-cms-pluginbase": "^1.1.0",
    "egg-born-module-cms-pluginarticle": "^1.0.0",
    "egg-born-module-cms-pluginsidebar": "^1.0.0",
    "egg-born-module-cms-pluginmarkdowngithub": "^1.0.0",
    "egg-born-module-cms-plugintrack": "^1.0.1"
  • “theme”: true, To declare it is a theme module

Install theme

$ npm i egg-born-module-cms-themeblog

Rendering template

Name Instruction Rendering timing Remarks
assets Resource files One-time
layout Layout directory Intermediate file The layout is not an officially mandated directory. The theme can be added according to your needs, and you could planning your own page elements as well
main Main rendering template directory Two rendering timings
main/article.ejs Article rendering template Use this template file when you need to render an article
main/index Home page rendering template directory Use the template files in this directory when you need to render the home page. Why is directory? In a complex site, you can have multiple classes of home page templates depending on the scenario.
static Static file directory One-time build For example, the file articles.ejs, gets articles by calling the backend API with ajax, thereby implements directory, tag, search and other functions concentratedly
  • The theme cms-themeblog provides three main rendering templates:
    • static/articles.ejs: Used to implemente functions like directories, tags, search, etc concentratedly
    • main/article.ejs: Used for article rendering
    • main/index/index.ejs: Used for home page rendering
  • assets is the resource directory: Contains resources such as CSS, JS, and Image
  • layout is the layout directory: Contains rendering template of layout elements

In fact, most of the rendering template files in the layout are very streamlined, why not merge into one rendering file, but to be so detailed?

  • The layout elements are splited in detail, so that in the actual use of theme, less things need to do in custom part, and it is easier to upgrade to the updated version of the theme as well.

Main rendering template


<%- await include('../layout/header.ejs') %>
<div class="row">
  <div class="col-sm-8">
      <%- await include('../layout/breadcrumb.ejs') %>
      <ul class="article-list media-list">
  <div class="col-sm-4">
    <%- await include('../layout/sidebar.ejs') %>
<%- await include('../layout/footer.ejs') %>


<%- await include('../layout/header.ejs') %>
<div class="row">
  <div class="col-sm-8">
      <%- await include('../layout/breadcrumb.ejs') %>
      <%- await include('../layout/article/title.ejs')%>
      <%- article.html %>
      <%- await include('../layout/article/attachments.ejs') %>
      <%- await include('../layout/article/stat.ejs') %>
      <%- await include('../layout/article/brothers.ejs') %>
      <%- await include('../layout/article/comments.ejs') %>
  <div class="col-sm-4">
    <%- await include('../layout/sidebar.ejs') %>
<%- await include('../layout/footer.ejs') %>


<%- await include('../../layout/header.ejs') %>
<div class="row">
  <div class="col-sm-8">
      <%- await include('../../layout/breadcrumb.ejs') %>
      <ul class="article-list media-list">
          // options
          const options = {
            where: {
              'f.language': site.language.current,
            orders: [
              [ 'f.sticky', 'desc' ],
              [ 'a.createdAt', 'desc' ],
            page: { index:0 },
            mode: 'list',
          // select
          const data=await ctx.performAction({
            url: '/a/cms/article/list',
            query: { options:JSON.stringify(options) },
          // index
          // list
          for(const item of data.list){
             const sticky = !item.sticky ? '' : '<span class="glyphicon glyphicon-pushpin"></span> ';
             const attachment = item.attachmentCount===0 ? '' : '<span class="glyphicon glyphicon-paperclip"></span> ';
             const media = !item.imageFirst ? '' : `
<div class="media-right">
      <a target="_blank" href="${url(item.url)}">
        <img class="media-object img-delay" data-src="${item.imageFirst}" data-width="125" data-height="100">
        // tags
        let tagsText='';
        const tags=item.tags?JSON.parse(item.tags):null;
        if(tags && tags.length>0){
          tagsText+='<span class="num glyphicon glyphicon-tags"></span> ';
          for(const tag of tags){
            tagsText+=`<a target="_blank" href="${url('static/articles.html')}?tagId=${}&tagName=${}"><span class="num tag">${}</span></a>`;
        // stat
        const stat=`
<div class="title stat" data-article-id="${item.atomId}">
<a target="_blank" href="${url('static/articles.html')}?categoryId=${item.categoryId}&categoryName=${item.categoryName}"><span class="num category">${item.categoryName}</span></a>
<span class="num date">${util.formatDateTime(item.createdAt)}</span>
<span class="glyphicon glyphicon-eye-open"></span><span class="num readCount"></span>
<span class="glyphicon glyphicon-heart"></span><span class="num starCount"></span>
<a target="_blank" href="${url(item.url)}#comments"><span class="glyphicon glyphicon-comment"></span><span class="num commentCount"></span></a>

        const html= `
<li class="media">
    <div class="media-body">
      <h4 class="media-heading">${sticky}${attachment}<a target="_blank" href="${url(item.url)}">${item.atomName}</a></h4>
      ${item.description || item.summary}
  <div class="col-sm-4">
    <%- await include('../../layout/sidebar.ejs') %>
<%- await include('../../layout/footer.ejs') %>

The difference between static/articles.ejs and main/index/index.ejs

The layout of the two rendering templates are same, why should they be implemented respectively?

In order to take care of SEO optimization, homepage loading performance, article rendering performance, different rendering designs are made for different scenes.

  • The home page pre-renders the first page article list (the default is 20 articles), when scrolling down, loads the subsequent article list
  • In the directory, label, search scenario, there is no need to pre-render the first page article list
  • Therefore, main/index/index.ejs needs to support two rendering timings, while static/articles.ejs only needs one-time build

Layout rendering template

Here we focus on the rendering templates of header and footer. For other content, please check the module source directly.


<!DOCTYPE html>
  <%- await include('./header/head.ejs') %>
    <div class="container">
      <%- await include('./header/title.ejs') %>
      <%- await include('./header/menu.ejs') %>
  <div class="container container-site">


<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="generator" content="Cabloy-CMS" />
<link rel="shortcut icon" href="<%=url('assets/images/favicon.ico')%>" type="image/x-icon">

<%- await include('../../plugins/cms-pluginarticle/header/meta.ejs') %>

<%- await include('../../plugins/cms-pluginmarkdowngithub/header/css.ejs') %>
<%- await include('../../plugins/cms-pluginbase/header/css.ejs') %>
<%- await include('../../plugins/cms-pluginarticle/header/css.ejs') %>
<%- await include('../../plugins/cms-pluginsidebar/header/css.ejs') %>

<%- await include('../../plugins/cms-pluginbase/header/js.ejs') %>
<%- await include('../../plugins/cms-pluginarticle/header/js.ejs') %>
<%- await include('../../plugins/cms-pluginsidebar/header/js.ejs') %>

<link rel="stylesheet" href="" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="_ _CSS_ _">
_ _ENV_ _

<!--[if lt IE 9]>
  <script src=""></script>
  <script src=""></script>

  // pluginbase init

  // css
  // js
  • Comprise plugin template files via include, such as cms-pluginbase
  • Declare JS files via js
  • Declare CSS files via css
  • _ _CSS_ _: CSS file placeholder
  • _ _ENV_ _: frontend environment object placeholder


  </div> <!-- container -->
      <span class="small">Powered by <a target="_blank" href="">Cabloy-CMS</a> | Theme - <a target="_blank" href="<%=site._theme.url%>"><></a></span>
  <script src=""></script>
  <script src="" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
  <script src="_ _JS_ _"></script>
  <%- await include('../plugins/cms-plugintrack/track.ejs') %>
  • _ _JS_ _: JS file placeholder