How to Build a blog with Astro, Strapi, & Tailwind CSS

This article has been update to use Astro 3 and Strapi 4 by Paul Bratslavsky.

This is a companion discussion topic for the original entry at

" Navigate and open post.js , in api\post\models\post.js model and copy the following lines of code:"

There isn’t a directory named models inside of api\post

It doesn’t exist so I created one…and it didn’t work.

Thanks for the tut but after messing with Strapi I think I will just find a different headless cms.

It seems the author didn’t follow his own tutorial, I agree with chokeul8r, since I also can’t find /models/ folder. I did everything according this tutorial and I have a 500 error.

  1. Error: The following dependencies are imported but could not be resolved: date-fns & react-markdown (ok, i’ve added it)
  2. Unable to resolve a renderer that handles JSX transforms! Please include a renderer plugin which supports JSX in your astro.config.mjs file.
  3. Unable to install reading progress

I think Strapi should correct this tutorial or delete it, if you don’t want to lose your novice users.

Thanks for the heads-up! We’ll make the corrections and keep you posted.

1 Like

Hi there. This looks like a great product, but first exposure to it, through this tutorial, has been frustrating - to say the least.
Tutorials are fantastic ideas as they demonstrate use cases and workflow, but only useful if they actually work!
In fact, if they don’t work, they are worse than useless; as they alienate the user towards the product from the off.
In this tutorial, as well as not fixing the comments above, which you said you would do 26 days ago, there are quite a few other breaking problems:

  1. You constantly refer to the Strapi endpoint as localhost:1337/posts, both in the text and the code. It is not. It actually is localhost:1337/api/posts - The one you specify just returns a 404.

  2. The screen shots of setting user permissions do not match the actual screen - in the version I am using (4.0.7) it is completely different to you screenshots. Also, there is no count option for posts on my version.

  3. I have had to add
    renderers: [
    into the astro.config.mjs file to deal with the error about ‘please include a rendered to deal with JSX’ error mentioned in an earlier comment.

  4. And, after all that, it still doesn’t work! I now get ‘No blog posts found’ when it all runs. But if I go to http://localhost:1337/api/posts in my browser I can clearly see the JSON for 5 blog posts I have created.

  5. I cannot get the lifecycle hooks to work, even after creating the api\post\models\post.js file and folder which do not exist.

PLEASE can someone actually go through this tutorial and DO IT, to check it actually works? Obviously, no one has, which is poor.

Then please correct all the errors including the ones I’m sure I haven’t found. Strapi looks like a fab product. I Really want to use it. But this does not bode well for detail or support. Thanks.

This is absolutely infuriating.

I really want to like Strapi and start using it …

but I can NOT get a single tutorial to work. I follow all the steps, and half way through, it’s NOT working.

PLEASE mention what version of Strapi the code was written for. I have v4.0.7.

PLEASE proofread your material.

PLEASE provide a Github repo for downloading WORKING SOURCE CODE!

PLEASE PLEASE PLEASE disable that ridiculous JavaScript that prevents users from Copying the code from the code blocks. I can copy it by digging into the DevTools.


1 Like

HI Can you provide the link for the revised version of this, I see if we follow the original article it is not working. Thanks

1 Like

This is really frustrating. Although the blog shows that this article was updated recently, the problems others already addressed are still there. I’m stuck at the reading time part. The /models/ folder doesn’t exist, and when I create it nothing happens. if i create a file named lifecycles.js in post/content-types/, I get errors.

For anyone who is trying to make the reading time work, here’s a solution:
in src/api/post/content-types/post/lifecycles.js:

"use strict";
const readingTime = require("reading-time");
module.exports = {
    async beforeCreate(event) {
      const { data } = event.params;
      if ( && > 0) { = readingTime( || null;
    async beforeUpdate(event) {
      const { data } = event.params;
      if ( && > 0) { = readingTime( || null;

Could you explain it more detailed please or show some example. It is still doesn’t working :cry:

For example lifecycles.js want there so I created one

Update: It works, I forget to add field readingTime to content

1 Like

In case anyone still looking for answers on how to enable read time functionality follow these steps and it should work;

1 - Run this in your headlesss-blog repo to install reading-time package.

npm install reading-time --production

2 - Add a new file “lifecycles.js” in the following path;


3 - Add this code to lifecycles.js;

const readingTime = require("reading-time");

module.exports = {
  beforeCreate(event) {
    const { data, where, select, populate } = event.params;

    if ( && > 0) {
      // @ts-ignore = readingTime( || null;

  beforeUpdate(event) {
    const { data, where, select, populate } = event.params;

    if ( && > 0) {
      // @ts-ignore = readingTime( || null;


It appears that they’ve changed file structure multiple times, the article has links to explain the new structure.

1 Like

Thank for the solution.

I just recently updated this article to use Astro 3 and Strapi v4 since having similar issues. Sorry for your frustration. If you have questions let me know.

If you are still interested you can find the update project here GitHub - PaulBratslavsky/strapi-astro-blog-post and a video walkthrough how to get it up and running.

Hi Paul. Little help?

When trying to run Astro I’m seeing the following error:

  const postImage = || null;

TypeError: Cannot read properties of undefined (reading 'url')
    at eval (/Users/****/strapi/headless-blog/astro-blog/src/components/BlogGridItem.astro:14:51)
    at AstroComponentInstance.BlogGridItem [as factory] (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/astro-component.js:21:12)
    at AstroComponentInstance.init (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/render/astro/instance.js:32:29)
    at AstroComponentInstance.render (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/render/astro/instance.js:37:18)
    at Object.render (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/render/component.js:338:22)
    at Module.renderChild (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/render/any.js:36:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Object.renderToFinalDestination (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/render/util.js:119:7)
    at async RenderTemplateResult.render (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/render/astro/render-template.js:45:9)
    at async renderChild (/Users/****/strapi/headless-blog/astro-blog/node_modules/astro/dist/runtime/server/render/any.js:36:5)

Node.js v21.1.0

Any tips?

Thank you.

You should tell and notice everyone that using getStaticPaths with have an effect on the js bundle created.

read this for more info: Include <script> tag only when component used · withastro/roadmap · Discussion #664 · GitHub

if you are to use Astro, i would highly recommend NOT using strapi.

Hi @jimtyrro,

You can try this.

const postImage = featuredImage?.data?.attributes?.url || null;

Thank you, that fixed it.

1 Like

Nice to hear that you fixed it . The error was there is when one of the attributes are not filled in fields, it get it as undefined. Then it gives an error like that. Adding “?” it doesn’t give any error if it exist or not.

1 Like

Thanks for the answer. It’s good to add a check if data exists and return null.