Strapi : ctx.render is not a function

Frontend.LA Engineers

Jan, 04

Strapi is marketed as the greatest node.js headless CMS of all times.

Hmmm we think it is trying to do the same thing that StrongLoop did 6-7 years ago with one added functionality - "CRUD admin views" but if you are a dev oh sorry an engineer who is putting in the effort to

  1. Go through Strapi's docs
  2. Go through their open issues on Github
  3. Figuring out how to call Strapi API in your React/Vue/Angular/Svelte app
  4. Googling errors as you encounter 
  5. Figuring out if Strapi can serve views
  6. Learning what strapi-hook-ejs is all about
  7. Googling more if Strapi provides hooks for jade/pug
  8. Not finding answers

hmm, then I question your choice of going with Strapi, to begin with. What did you want to accomplish that made you think Strapi was the right tool for the job? If you wanted to save time cuz you needed APIs auto-generated then you realized it is eating up more time to figure everything else out.

As we mentioned before StrongLoop did this years ago with their product called "loopback" which to date has a lot more features than Strapi. But the IBM acquisition made it an unattractive choice even though it's still free.


Other alternatives of Strapi :

1. React Admin (Auto-generated CRUD views in React + Material UI) 

2. Hasura (GraphQL)

3. Parse.js


Sure there are more but you need to step back and rethink what are you trying to accomplish?


Now Strapi has raised a lot of money - , it's probably going to take the same route that StrongLoop did (acquired by IBM) just so investors can recover their money because there is no way Strapi will generate $4M considering ppl have multiple options and Wordpress being one of them with a free node.js client to CRUD it all.


Anyhow, back to solving the Strapi EJS hooks ctx.render is not a function problem.


When we tried using stripe-hooks-ejs their page said its a "built-in" hook

but it's not, but yeah they have $4m now don't worry docs will be fixed. Stay on course.

First thing to try:

npm i [email protected] --save


yarn add [email protected]

Ugh, when can we stop writing this npm i or yarn add?

Did that fix it?

If not, other things to check

2. Did you add this code to hooks.json?

  "ejs": {
    "enabled": true,
    "layout": "layout",
    "viewExt": "ejs",
    "partial": true,
    "cache": false,
    "debug": true

still nothing?

3. Did you create a controller?

strapi generate:controller dummy

and this code to it?

module.exports = {
  home: async ctx => {
    return ctx.render('home', {
      title: 'My app title',

still boo boo?

4. Did you create a views folder and add two views?

 4.1. home.ejs with one line <%= title %>

 4.2. layout.ejs

5. restart server?

Still the same? You're kidding?

If you are still facing the issue then take the advice of experts -(random ppl on

1. Use strapi as an API and keep it separate from your Frontend app (React/Angular/Vue/Svelte/HTML + jQuery)

2. If you don't know any of these and only know ejs/pug then create an express server with one line to serve ejs/pug views and call Strapi API internally to consume the content.

What did we do?

Moved away from Strapi. The POC for a simple use case was great and all the best for the Strapi team but the product doesn't seem mature enough.

1. We could not restrict certain fields from showing in the API

2. They suggest using graphQL for that which solves the problem by selecting a few fields but it doesn't solve the problem that anyone can hit the rest endpoint and see all the fields 

3. The server keeps restarting when we change something in the ejs views and there is no way that we found to ignore the views folder to be watched + as you may have noticed restarts are not quick