Hiding API Key by Adding Custom Environment Variables 🔐🔑
So far, we've been calling our News API with the API key written directly inside our code.
That's a problem — if we push this code to GitHub, anyone can see our secret key in the
commit history. In this tutorial, we'll learn the right way to hide sensitive values like
API keys using environment variables in a React app.
In this tutorial, we will learn:
What environment variables are and why they matter for security
Creating a .env file in a React project
Why React environment variables must start with REACT_APP_
Reading environment variables using process.env
Adding .env to .gitignore so it never reaches GitHub
Restarting the dev server so new variables take effect
---
What Are Environment Variables?
An environment variable is a value that lives outside your source code, usually in a
special file, and gets injected into your app at build or run time. Instead of hardcoding a
secret like an API key directly in News.js, we store it in a .env file that
never gets shared publicly. This keeps sensitive data safe even if your code is open source.
---
Step 1: Create a .env File
In the root of your React project (same level as package.json), create a new file
named .env.
REACT_APP_NEWS_API_KEY=your_actual_api_key_here
Important: In Create React App projects, every custom environment variable
must start with REACT_APP_, otherwise React will ignore it completely.
---
Step 2: Add .env to .gitignore
We never want to push our .env file to GitHub. Open (or create) the
.gitignore file in your project root and add this line:
# Environment variables
.env
If you already committed .env before adding this line, remove it from Git
tracking with git rm --cached .env and commit again.
---
Step 3: Access the API Key with process.env
Now, in News.js, replace the hardcoded key with a reference to
process.env.REACT_APP_NEWS_API_KEY.
fetchArticles = () => {
this.setState({ loading: true });
const apiKey = process.env.REACT_APP_NEWS_API_KEY;
const url = `https://newsapi.org/v2/top-headlines?country=us&page=${this.state.page}&pageSize=${this.state.pageSize}&apiKey=${apiKey}`;
fetch(url)
.then((response) => response.json())
.then((data) => {
this.setState({
articles: data.articles,
loading: false,
});
});
};
---
Step 4: Restart the Development Server
React only reads .env values when the dev server starts. If your app is already
running, stop it and run npm start again — otherwise your new environment
variable will show up as undefined.
---
Step 5: Full Updated News.js
Here is the complete News.js file with the API key now loaded securely from an
environment variable instead of being hardcoded.
import React, { Component } from "react";
import NewsItem from "./NewsItem";
import Spinner from "./Spinner";
export class News extends Component {
static defaultProps = {
pageSize: 6,
totalResults: 30,
};
state = {
articles: [],
page: 1,
loading: false,
pageSize: this.props.pageSize,
};
fetchArticles = () => {
this.setState({ loading: true });
const apiKey = process.env.REACT_APP_NEWS_API_KEY;
const url = `https://newsapi.org/v2/top-headlines?country=us&page=${this.state.page}&pageSize=${this.state.pageSize}&apiKey=${apiKey}`;
fetch(url)
.then((response) => response.json())
.then((data) => {
this.setState({
articles: data.articles,
loading: false,
});
});
};
componentDidMount() {
this.fetchArticles();
}
handlePrevClick = () => {
this.setState({ page: this.state.page - 1 }, () => {
this.fetchArticles();
});
};
handleNextClick = () => {
this.setState({ page: this.state.page + 1 }, () => {
this.fetchArticles();
});
};
handlePageSizeChange = (e) => {
const newSize = Number(e.target.value);
this.setState({ pageSize: newSize, page: 1 }, () => {
this.fetchArticles();
});
};
render() {
const { totalResults } = this.props;
const { page, pageSize, articles, loading } = this.state;
return (
<div className="container my-4">
<h2 className="text-center mb-4">Welcome to News App 📰</h2>
<div className="d-flex justify-content-end mb-3">
<label className="me-2 align-self-center fw-bold">Articles per page:</label>
<select
className="form-select w-auto"
value={pageSize}
onChange={this.handlePageSizeChange}
>
<option value="3">3</option>
<option value="6">6</option>
<option value="9">9</option>
<option value="12">12</option>
</select>
</div>
{loading ? (
<Spinner />
) : (
articles.map((article, index) => (
<NewsItem
key={index}
title={article.title}
description={article.description}
imageUrl={article.urlToImage}
newsUrl={article.url}
/>
))
)}
<div className="d-flex justify-content-between my-4">
<button
className="btn btn-warning"
onClick={this.handlePrevClick}
disabled={loading || page <= 1}
>
← Previous
</button>
<span className="align-self-center fw-bold">
Page {page} of {Math.ceil(totalResults / pageSize)}
</span>
<button
className="btn btn-warning"
onClick={this.handleNextClick}
disabled={loading || page >= Math.ceil(totalResults / pageSize)}
>
Next →
</button>
</div>
</div>
);
}
}
export default News;
---
Environment Variable Quick Reference
| File / Code |
Purpose |
Notes |
.env |
Stores the actual API key value locally |
Must sit in the project root, never committed to Git |
REACT_APP_ prefix |
Tells Create React App to expose the variable to the frontend build |
Variables without this prefix are ignored |
process.env.REACT_APP_NEWS_API_KEY |
Reads the key inside your component code |
Undefined until the dev server is restarted after edits |
.gitignore |
Prevents .env from being pushed to GitHub |
Add .env as its own line |
---
Features and Learnings:-
Understood what environment variables are and why hiding API keys matters.
Created a .env file to store the News API key outside of source code.
Learned why React requires the REACT_APP_ prefix for custom variables.
Accessed the hidden key safely using process.env.
Protected the key from GitHub by adding .env to .gitignore.
Restarted the dev server so environment changes took effect.
Connected the News App to the live News API using the hidden key.