# Dynamic blog rendering for regular react/typescript app

**URL:** <https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638>\
**Category:** CMS\
**Tags:** react\
**Created:** [May 21, 2024, 10:17pm UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638 "2024-05-21T22:17:44Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![cedson](https://avatars.discourse-cdn.com/v4/letter/c/e99b99/32.png) [@cedson](https://forum.builder.io/u/cedson)\
**Post date:** [May 21, 2024, 10:17pm UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638/1 "2024-05-21T22:17:44Z")

</div>

Hello, I am attempting to recreate our app, but in react/typescript. I’m trying to have a dynamic blog app and recreating the following blog post but in react - [Create a drag-and-drop editable blog with Next.js](https://www.builder.io/blog/creating-blog). I can get the section model to render, but not render specific blogs when I search it. When checking the console network tab, I can see if querying the data model, and retrieving the data, but it is the wrong slug. The environment is a separate one than this to start fresh but here is the link: [Builder.io: Visual Development Platform](https://builder.io/content/74354cd668084c7db651298abcefbe9b/edit?activeDesignerTab=5)

Here is the code as well

```auto
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { BuilderComponent, builder, useIsPreviewing } from '@builder.io/react';
import axios from 'axios';
import styles from './blog.module.css';

builder.init(*removed for privacy*);

interface ArticleData {
  data: any; // Replace with the specific type based on your data structure
}

interface ArticleTemplate {
  data: any; // Replace with the specific type based on your template data structure
}

export function Blog() {
  const { slug } = useParams<{ slug: string }>();
  const isPreviewingInBuilder = useIsPreviewing();
  const [notFound, setNotFound] = useState(false);
  const [articleData, setArticleData] = useState<ArticleData | null>(null);
  const [articleTemplate, setArticleTemplate] = useState<ArticleTemplate | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchContent() {
      try {
        const articleResponse = await builder
          .get('article', {
            options: { includeRefs: true },
            query: {
              'data.slug': slug,
            },
          })
          .toPromise();
          console.log(articleResponse)
          

        const templateResponse = await builder
          .get('blog-template', {
            options: { enrich: true },
          })
          .toPromise();

        if (articleResponse && templateResponse) {
          setArticleData(articleResponse);
          setArticleTemplate(templateResponse);
        } else {
          setNotFound(true);
        }
      } catch (error) {
        console.error('Error fetching data:', error);
        setNotFound(true);
      } finally {
        setLoading(false);
      }
    }

    fetchContent();
  }, [slug]);

  if (loading) {
    return <h1>Loading...</h1>;
  }

  if (notFound && !isPreviewingInBuilder) {
    return <div>Not Found 404 Error</div>;
  }

  return (
    <div className={styles.container}>
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      {!articleData && <meta name="robots" content="noindex" />}
      {articleData && articleTemplate && (
        <BuilderComponent
          model="blog-template"
          content={articleTemplate}
          data={{ article: articleData.data }}
        />
      )}
    </div>
  );
};

```

The app routes are as follows:

```auto
 <Routes>
        <Route path="/" element={<HomePage />}></Route>
        <Route path="/builder" element={<CatchAllRoute />}></Route>
        <Route path="/builder/*" element={<CatchAllRoute />}></Route>
        <Route path="/blog/" element={<Blog />}></Route>
        <Route path="/blog/*" element={<Blog />}></Route>
      </Routes>

```

I do have dynamic rendering for the initial builder path, but not the blog path

---

<div class="post-metadata">

**Author:** ![anon43891756](https://avatars.discourse-cdn.com/v4/letter/a/50afbb/32.png) [@anon43891756](https://forum.builder.io/u/anon43891756)\
**Post date:** [May 22, 2024, 6:03am UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638/2 "2024-05-22T06:03:28Z")

</div>

Hi @cedson, Hope you are doing well!

Let’s go through your code to understand the issue and ensure it is configured correctly for fetching and rendering specific blog posts dynamically with React and TypeScript in a [Builder.io](http://Builder.io) environment.

### Step-by-Step Debugging and Fixing:

1. **Initialization and Configuration** :

2. **Slug Matching** :

3. **API Query and Response** :

### Example Improvements and Validation:

**Complete and Improved Example** :

```typescript
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { BuilderComponent, builder, useIsPreviewing } from '@builder.io/react';
import styles from './blog.module.css';

builder.init('YOUR_PUBLIC_API_KEY');

interface ArticleData {
  data: {
    slug: string;
    title: string;
    // Add any other fields your article content model contains
    content: any;
  };
}

interface ArticleTemplate {
  data: any; // Replace with the specific type based on your template's data structure
}

export function Blog() {
  const { slug } = useParams<{ slug: string }>();
  const isPreviewingInBuilder = useIsPreviewing();
  const [notFound, setNotFound] = useState(false);
  const [articleData, setArticleData] = useState<ArticleData | null>(null);
  const [articleTemplate, setArticleTemplate] = useState<ArticleTemplate | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchContent() {
      try {
        // Fetch the specific article by slug
        const articleResponse = await builder
          .get('article', {
            options: { includeRefs: true },
            query: {
              'data.slug': slug,
            },
          })
          .toPromise();
        console.log('Article Response:', articleResponse);

        // Fetch the blog template
        const templateResponse = await builder
          .get('blog-template', {
            options: { enrich: true },
          })
          .toPromise();
        console.log('Template Response:', templateResponse);

        if (articleResponse && articleResponse.length > 0 && templateResponse) {
          setArticleData(articleResponse[0]);
          setArticleTemplate(templateResponse);
        } else {
          setNotFound(true);
        }
      } catch (error) {
        console.error('Error fetching data:', error);
        setNotFound(true);
      } finally {
        setLoading(false);
      }
    }

    fetchContent();
  }, [slug]);

  if (loading) {
    return <h1>Loading...</h1>;
  }

  if (notFound && !isPreviewingInBuilder) {
    return <div>Not Found 404 Error</div>;
  }

  return (
    <div className={styles.container}>
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      {!articleData && <meta name="robots" content="noindex" />}
      {articleData && articleTemplate && (
        <BuilderComponent
          model="blog-template"
          content={articleTemplate}
          data={{ article: articleData.data }}
        />
      )}
    </div>
  );
};

```

### Key Points to Note:

1. **Initialization** :

2. **Slug Matching and Query** :

3. **Check Network Requests** :

4. **Handling Responses** :

5. **Error Handling and Debugging** :

### Testing and Debugging:

1. **Network Tab** :

2. **Console Logging** :

By following these steps and ensuring the data fetched is correctly handled and matched to your expected structure, you should be able to get the blog posts to display correctly. If issues persist, dive deeper into the responses in the network tab and adjust the query or data parsing as necessary.

---

<div class="post-metadata">

**Author:** ![cedson](https://avatars.discourse-cdn.com/v4/letter/c/e99b99/32.png) [@cedson](https://forum.builder.io/u/cedson)\
**Post date:** [May 23, 2024, 10:09pm UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638/3 "2024-05-23T22:09:02Z")

</div>

Hi Garima, I got this to work. The page is able to dynamically render the content based upon the slug.

My new question is, if I don’t have a slug and just want to do localhost:4200/blog, the blog home page, how do I render a separate page rather than the blog article content?

Right now, The same page renders if I have a slug or not, but I have a text box that is doing an element binding to the articleData property which is being queried from the slug. If I have no slug, obviously no data is showing, if I do have a slug, the data is showing. Which is great, but I want to render a completely different “home page” If I don’t have a second slug passed in. Do I need to implement a whole new page and then just render the “section” if there is a slug?

---

<div class="post-metadata">

**Author:** ![anon43891756](https://avatars.discourse-cdn.com/v4/letter/a/50afbb/32.png) [@anon43891756](https://forum.builder.io/u/anon43891756)\
**Post date:** [May 24, 2024, 11:18am UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638/4 "2024-05-24T11:18:07Z")

</div>

@cedson

To render a different “home page” for your blog when there is no slug provided (e.g., when visiting `localhost:4200/blog`), and render specific blog article content when a slug is provided (e.g., `localhost:4200/blog/[slug]`), you can set up routing in your React application to handle these cases differently.

Here’s a detailed explanation of how you can achieve this:

### Setting Up Routes

You can use a routing library such as `react-router-dom` to manage your routes. You would set up routes to render different components based on whether a slug is provided.

### Example Implementation with React Router

**1. Setup Routes in App Component**

**App.js**

```javascript
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import BlogPage from './BlogPage';
import BlogArticle from './BlogArticle';

function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/blog" component={BlogPage} />
        <Route path="/blog/:slug" component={BlogArticle} />
      </Switch>
    </Router>
  );
}

export default App;

```

**2. Implement BlogPage Component for the Blog Home Page**

**BlogPage.js**

```javascript
import React, { useEffect, useState } from 'react';
import { BuilderComponent, builder } from '@builder.io/react';

builder.init('YOUR_PUBLIC_API_KEY');

const BlogPage = () => {
  const [homeContent, setHomeContent] = useState(null);

  useEffect(() => {
    const fetchContent = async () => {
      const content = await builder.get('page', {
        query: {
          'data.slug': 'blog-home'
        }
      }).toPromise();
      setHomeContent(content);
    };

    fetchContent();
  }, []);

  return (
    <div>
      {homeContent ? (
        <BuilderComponent model="page" content={homeContent} />
      ) : (
        <p>Loading...</p>
      )}
    </div>
  );
};

export default BlogPage;

```

**3. Implement BlogArticle Component for Individual Articles**

**BlogArticle.js**

```javascript
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { BuilderComponent, builder } from '@builder.io/react';

builder.init('YOUR_PUBLIC_API_KEY');

const BlogArticle = () => {
  const { slug } = useParams();
  const [articleData, setArticleData] = useState(null);

  useEffect(() => {
    const fetchContent = async () => {
      const content = await builder.get('article', {
        query: {
          'data.slug': slug
        }
      }).toPromise();
      setArticleData(content);
    };

    fetchContent();
  }, [slug]);

  return (
    <div>
      {articleData ? (
        <BuilderComponent model="article" content={articleData} />
      ) : (
        <p>Loading...</p>
      )}
    </div>
  );
};

export default BlogArticle;

```

### Explanation

1. **Routing Setup**

2. **BlogPage Component**

3. **BlogArticle Component**

### Notes

- **Builder Content Models** :

- **Error Handling** :

By following this approach, you can effectively render different content for the blog home page and individual articles based on the presence of a slug in the URL. This allows you to manage both views flexibly using [Builder.io](http://Builder.io) and React.

Let me know if you’ve further questions!

---

<div class="post-metadata">

**Author:** ![cedson](https://avatars.discourse-cdn.com/v4/letter/c/e99b99/32.png) [@cedson](https://forum.builder.io/u/cedson)\
**Post date:** [May 24, 2024, 9:23pm UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638/5 "2024-05-24T21:23:36Z")

</div>

This is great, thank you!

---

<div class="post-metadata">

**Author:** ![cedson](https://avatars.discourse-cdn.com/v4/letter/c/e99b99/32.png) [@cedson](https://forum.builder.io/u/cedson)\
**Post date:** [May 30, 2024, 9:53pm UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638/6 "2024-05-30T21:53:11Z")

</div>

Following up here. Looking for help on registering custom components to builder. I followed the steps for react and it wasn’t working. Steps I took:

1. I created a custom component in my components folder.

2. I tried implementing and registering into my codebase, below:

```import
import { BuilderComponent, builder, useIsPreviewing, Builder } from '@builder.io/react';
import {NavBar2} from '../../../navbar2/src/lib/navbar2'
import './builderio.module.css'
import HelloWorld from '../../../../src/app/components/customComponent1/customComponent1';
builder.init('insert private key');

Builder.registerComponent(HelloWorld, {
  name: 'HelloWorld',
  inputs: [
    {
      name: 'text',
      type: 'text',
      defaultValue: 'World',
    },
  ],
});

export function CatchAllRoute() {
  const isPreviewingInBuilder = useIsPreviewing();
  // Use `any` to handle dynamic content structure
  const [notFound, setNotFound] = useState<boolean>(false);
  const [content, setContent] = useState<any | null>(null);

  useEffect(() => {
    async function fetchContent() {
      const content = await builder.get('page', {
        url: window.location.pathname
      }).promise();
      console.log(content);
      
      // Dynamically renders tab title if the content has a title
      if (content && content.data && content.data.title) {
        document.title = content.data.title;
      }

      setContent(content);
      setNotFound(!content);
    }
    fetchContent();
  }, []);

  if (notFound && !isPreviewingInBuilder) {
    return <div>Not Found 404 Error</div>;
  }

  return (
    <>
      <NavBar2 />
      {content && <BuilderComponent model="page" content={content} />}
    </>
  );
}

// src/builder.js

```

Here is the custom component code, very simple but just trying to implement to start.

```auto
import React from 'react';

const HelloWorld = (props:any) => {
  return <div>Hello, {props.text}!</div>;
};

export default HelloWorld;

```

When I do this the page doesn’t load. Please let me know what I’m doing wrong.

I am using typescript & react for my tech specs.

---

<div class="post-metadata">

**Author:** ![anon43891756](https://avatars.discourse-cdn.com/v4/letter/a/50afbb/32.png) [@anon43891756](https://forum.builder.io/u/anon43891756)\
**Post date:** [June 14, 2024, 6:29pm UTC](https://forum.builder.io/t/dynamic-blog-rendering-for-regular-react-typescript-app/5638/7 "2024-06-14T18:29:30Z")

</div>

Hey @cedson steps you followed seem correct. I opened the editor link you shared before but seems it running on localhost so won’t be able to debug the loading issue.

Please share the latest builder content link if it’s not on localhost else do you see any console error when the page not loading?
