> For the complete documentation index, see [llms.txt](https://cll-devrel.gitbook.io/javascript-for-blockchain-master-class/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cll-devrel.gitbook.io/javascript-for-blockchain-master-class/building-a-frontend.md).

# Building A Frontend

We've got the contract setup and use [Chainlink Functions](https://functions.chain.link/) to pull in the latest news article. We can see the titles, links, and dates published in Remix. That's awesome but not super user-friendly. Let's add a simplistic front end to present this data.&#x20;

### 🚀 Initiate Liftoff

{% hint style="success" %}
All of the code for this project can be found on [GitHub](https://github.com/smartcontractkit/workshop-distributed-news)
{% endhint %}

We'll be using [Astro](https://astro.build) to build out the frontend. Let's create a skeleton project.

`npm create astro@latest`

<figure><img src="/files/N1AwcPP61Qsl2TUKeksN" alt=""><figcaption></figcaption></figure>

Head to `cd ./distributed-news` and run `npm run dev`. You should now be able to access Astro at <http://localhost:4321>, if you visit that site, **Astro** should greet you. The basics are in place.&#x20;

### Read From The Blockchain

To read our data from the smart contract we deployed, we'll need to create 2 files. To do that, we must install a few NPM packages first. If your server is running, stop it with `Ctrl-C`, and run&#x20;

```
npm install ethers dotenv node-fetch jsdom
```

to add them to our project. Now, we can move to creating our files.&#x20;

&#x20;First, we'll get the ABI (Application Binary Interface) from Remix. This will allow `ethers.js` to know how to interact with our contract. To find the ABI, head back to [Remix](https://remix.ethereum.org/).

Under the compiler tab, look for the copy ABI button at the bottom.&#x20;

<figure><img src="/files/teuzWMgIRn95jHT3DSNg" alt="" width="375"><figcaption></figcaption></figure>

Once you've copied the code, head to your code editor, create a new file, `distributed-news/src/lib/newsArchiveABI.json`, and paste the ABI.

Next, we'll need to create the script to pull the data off the blockchain. Create a new file, `distributed-news/src/lib/newsArchive.js` and add the following code.

```javascript
// Import necessary modules from ethers library
import { ethers, JsonRpcProvider } from "ethers";
// Import the dotenv module to load environment variables
import { config } from "dotenv";

// Import the ABI of the NewsArchive contract
import NewsArchiveABI from "./newsArchiveABI.json";

// Load environment variables from .env file
config();

// Get provider URL and contract address from environment variables
const providerUrl = process.env.PROVIDER_URL;
const contractAddress = process.env.CONTRACT_ADDRESS;

// Create a new JSON-RPC provider
const provider = new JsonRpcProvider(providerUrl);

// Create a new contract instance with the NewsArchive ABI
const newsArchiveContract = new ethers.Contract(
  contractAddress,
  NewsArchiveABI,
  provider
);

// Define an async function to get all articles from the contract
async function getAllArticles() {
  try {
    // Call the getAllArticles function of the contract
    const articles = await newsArchiveContract.getAllArticles();
    // Return the articles
    return articles;
  } catch (error) {
    // Log the error and return an empty array
    console.error("Error fetching articles:", error);
    return [];
  }
}

// Export the getAllArticles function as the default export
export default getAllArticles;
```

### Environment Variables

You may have noticed that we are using `dotenv` to load values for `PROVIDER_URL` and `CONTRACT_ADDRESS`, you'll need to create `distributed-news/.env` to house those values. Be sure to replace the value for `CONTRACT_ADDRESS` with the contract address you deployed.&#x20;

```dotenv
# RPC for sepolia
PROVIDER_URL="https://rpc.sepolia.org"
CONTRACT_ADDRESS="YOUR_CONTRACT_ADDRESS"
```

### Displaying The Results

One last step! We are almost there. We need to update `distributed-news/src/pages/index.astro` to display the articles we've saved.&#x20;

First, let's update the frontmatter.&#x20;

```javascript
---
import fetch from "node-fetch";
import { JSDOM } from "jsdom";
// Import the getAllArticles function from the newsArchive.js file
import getAllArticles from "../lib/newsArchive.js";

// Initialize articles variable and error flag
let articles;
let errorOccurred = false;

try {
  // Try to fetch all articles
  articles = await getAllArticles();
} catch (error) {
  // If an error occurs, log it and set the error flag
  console.error("Failed to get articles:", error);
  articles = [];
  errorOccurred = true;
}
async function fetchOGData(url) {
  try {
    const response = await fetch(url);
    const html = await response.text();
    const dom = new JSDOM(html);
    const doc = dom.window.document;
    let ogTitle =
      doc.querySelector('meta[property="og:title"]')?.content || url;
    const ogImage = doc.querySelector('meta[property="og:image"]')?.content;
    return { ogTitle, ogImage, url };
  } catch (error) {
    console.error("Error fetching OG data:", error);
    return { ogTitle: "No title", ogImage: "No image", url };
  }
}

async function articlesWithOGData(articles) {
  return await Promise.all(articles.map(fetchOGData));
}
let allArticles = await articlesWithOGData(articles);
---
```

Once that's updated we can use the variable `articles` to display them in the browser.\
We'll setup a conditional rendering block to check for any errors and if there are none, display the articles.&#x20;

```javascript
<html>
  <body>
    <h1>News Articles</h1>
    {
      // If an error occurred, display an error message
      // Otherwise, display the list of articles
      errorOccurred ? (
        <p>Sorry, we're unable to fetch articles at the moment.</p>
      ) : (
        <ul>
          {allArticles.map((article) => (
            <li>
              <article>
                <a href={article.url}>
                  <h2>{article?.ogTitle}</h2>
                </a>
                <img src={article?.ogImage} alt="" />
              </article>
            </li>
          ))}
        </ul>
      )
    }
  </body>
</html>
```

With these two changes you should see the results at <http://localhost:4321>\
Your front end should look something like this. \
![](/files/2JttEBZAbyTw4zUYLAae)

What it lacks in style, it makes up for in functionality.

### Add Some Style

You might be cringing at the lack of style, lets fix that and make things look just a little bit nicer.&#x20;

Below the `</html>` tag add in the following.&#x20;

```css
<style>
  body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
  }
  
  h1 {
    text-align: center;
  }
  
  ul {
    list-style-type: none;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    grid-gap: 20px;
  }
  
  article {
    background-color: #f4f4f4;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    transition: transform 0.3s ease;
  }
  
  article:hover {
    transform: translateY(-5px);
  }
  
  article a {
    text-decoration: none;
    color: #333;
  }
  
  article h2 {
    font-size: 18px;
    margin-top: 0;
  }
  
  article img {
    width: 100%;
    height: auto;
    border-radius: 4px;
    margin-top: 10px;
  }
</style>
```

Great, now, you've got some style!

### Congratulations!

Awesome! You've successfully deployed a smart contract using Chainlink Functions and connected it to a front end!&#x20;
