Implementation Guide

A quick and easy guide to getting NeuraFeed data to show up on your website.

1. Knowing What You Get

When you call the API, you get back an object that has a mix of plain text and HTML in it. Knowing which field is which is pretty important so nothing breaks on your page.

  • Plain Text: Fields like title, summary, whyItMatters, and tags are just regular strings so you can use them directly in your components.
  • HTML Content: The article field comes back as pre-formatted HTML. You need to inject it into the DOM instead of printing it out as raw text.
  • Sources: Each item in sources is a string that looks like [1] Site Name: https://.... You will need a small helper function to turn those into actual clickable links.
  • Cover Image: coverImage is a direct image URL (or null if unavailable). Always check for null before rendering. When you show it, link it to imageSourceUrl and label it with imageSource for attribution.
  • Embedded Media: embedMedia is either null or an object with a type value of youtube, id, title, and url. When present, you can embed the video using the standard YouTube embed URL. Always null-check before rendering.

2. Grabbing the Data

You can call the API from your backend, a server component, or basically any server-side code. Just do not call it directly from the browser because you will run into a CORS error. If you are using Next.js, an async Server Component is the easiest way to go since it fetches on the server and sends the finished HTML straight to the user.

async function getLatestNews() {
  const response = await fetch('https://feed.neuraspheres.com/api/latest-news');
  const data = await response.json();
  return data.article;
}

3. Building the UI

Pick whichever tab matches your setup. The React tab uses useState and useEffect to fetch on the client side, which works fine as long as the API has CORS set up for your domain. The Next.js tab fetches everything on the server so you never have to worry about CORS at all.

Both versions work the same way under the hood. They use dangerouslySetInnerHTML for the article body since it is already HTML, only render the tags and sources sections when there is actually something there, and use a parseSource helper that can handle both the labeled format [1] Site: URL and plain URLs.

import { useState, useEffect } from 'react';

function parseSource(rawSource) {
  const urlMatch = rawSource.match(/^\[(\d+)\]\s+(.+?):\s*(https?:\/\/\S+)/);
  if (urlMatch) {
    return { num: urlMatch[1], name: urlMatch[2], url: urlMatch[3] };
  }
  if (rawSource.startsWith("http")) {
    return { num: null, name: rawSource, url: rawSource };
  }
  return { num: null, name: rawSource, url: null };
}

export default function NewsViewer() {
  const [article, setArticle] = useState(null);

  useEffect(() => {
    fetch("https://feed.neuraspheres.com/api/latest-news")
      .then(res => (res.ok ? res.json() : null))
      .then(data => setArticle(data?.article ?? null))
      .catch(() => {});
  }, []);

  if (!article) return <div>No news found right now.</div>;

  return (
    <article>
      <h1>{article.title}</h1>
      <p>{article.summary}</p>

      {article.coverImage && (
        <a href={article.imageSourceUrl ?? article.coverImage} target="_blank" rel="noopener noreferrer">
          <img src={article.coverImage} alt={article.title} style={{ width: '100%', borderRadius: 8 }} />
          {article.imageSource && (
            <small style={{ display: 'block', textAlign: 'right', opacity: 0.6 }}>
              Source: {article.imageSource}
            </small>
          )}
        </a>
      )}

      <div dangerouslySetInnerHTML={{ __html: article.article }} />

      {article.embedMedia?.type === 'youtube' && (
        <div style={{ margin: '32px 0' }}>
          <iframe
            width="100%"
            height="400"
            src={`https://www.youtube.com/embed/${article.embedMedia.id}`}
            title={article.embedMedia.title}
            frameBorder="0"
            allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
            allowFullScreen
          />
        </div>
      )}

      {article.tags?.length > 0 && (
        <div>
          <h3>Tags</h3>
          {article.tags.map((tag, index) => (
            <span key={index}>#{tag} </span>
          ))}
        </div>
      )}

      {article.sources?.length > 0 && (
        <div>
          <h3>Sources</h3>
          <ul>
            {article.sources.map((sourceText, index) => {
              const { num, name, url } = parseSource(sourceText);
              return (
                <li key={index}>
                  {num && <span>[{num}] </span>}
                  {url ? <a href={url}>{name}</a> : name}
                </li>
              );
            })}
          </ul>
        </div>
      )}
    </article>
  );
}

4. Making It Look Good

The article body comes with its own HTML tags like h2, p, and sup already in it. You will probably want to write some CSS to style those so they match the rest of your site.

.article-container h2 {
  font-size: 24px;
  margin-top: 32px;
  font-weight: bold;
}

.article-container p {
  line-height: 1.6;
  margin-bottom: 16px;
}

.article-container sup {
  color: blue;
  font-size: 12px;
  vertical-align: super;
}

Wrapping Up

Both components already handle the case where article comes back as null, which can happen if the daily news has not been published yet or if there was a network problem. Other than that, you should be good to go!