Skip to content

Commit

Permalink
Merge pull request #92 from expatfile/@HofmannZ/feature/rsc-script
Browse files Browse the repository at this point in the history
✨ React Server Component script
  • Loading branch information
HofmannZ authored Nov 19, 2023
2 parents 844865d + d06bcc9 commit 675d5f8
Show file tree
Hide file tree
Showing 70 changed files with 4,513 additions and 648 deletions.
150 changes: 39 additions & 111 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,158 +1,86 @@
![GitHub branch checks state][build-url] [![codecov][cov-img]][cov-url] [![Known Vulnerabilities][snyk-img]][snyk-url]

# Next.js Runtime Environment Configuration
# 🌐 Next.js Runtime Environment Configuration

Populate your environment at **runtime** rather than **build time**.
**Effortlessly populate your environment at runtime, not just at build time, with `next-runtime-env`.**

- Isomorphic - Server and browser compatible (and even in middleware.)
- Next.js 13 App Router compatible.
- `.env` support during development, just like [Next.js][nextjs-env-vars-order].
🌟 **Highlights:**
- **Isomorphic Design:** Works seamlessly on both server and browser, and even in middleware.
- **Next.js 13 & 14 Ready:** Fully compatible with the latest Next.js features.
- **`.env` Friendly:** Use `.env` files during development, just like standard Next.js.

### Why we created this package 🤔
### 🤔 Why `next-runtime-env`?

[Build once, deploy many][build-once-deploy-many-link] is an essential principle
of software development. The main idea is to use the same bundle for all
environments, from testing to production. This approach enables easy deployment
and testability and is considered a
[fundamental principle of continuous delivery][fundamental-principle-link]. It
is also part of the [twelve-factor methodology][twelve-factor-link]. As crucial
as it is, it has yet to receive significant support in the world of front-end
development, and Next.js is no exception.
In the modern software development landscape, the "[Build once, deploy many][build-once-deploy-many-link]" philosophy is key. This principle, essential for easy deployment and testability, is a [cornerstone of continuous delivery][fundamental-principle-link] and is embraced by the [twelve-factor methodology][twelve-factor-link]. However, front-end development, particularly with Next.js, often lacks support for this - requiring separate builds for different environments. `next-runtime-env` is our solution to bridge this gap in Next.js.

Next.js does support [environment variables][nextjs-env-vars], but only at
build time. This means you must rebuild your app for each target environment,
which violates the principle. But what if you want, or need, to follow the build
once, deploy many principle?
### 📦 Introducing `next-runtime-env`

### This package 📦
`next-runtime-env` dynamically injects environment variables into your Next.js application at runtime. This approach adheres to the "build once, deploy many" principle, allowing the same build to be used across various environments without rebuilds.

`next-runtime-env` solves this problem by creating a context that exposes your environment variables at runtime, so you no longer have to declare
your environment variables at build time. The context provider safely exposes all environment variables prefixed with `NEXT_PUBLIC_` to the browser. This allows you to follow the build once, deploy many principle by providing differed runtime variables to the same build.
### 🤝 Compatibility Notes

### Compatibility 🤝
- **Next.js 14:** Use `[email protected]` for optimal caching support.
- **Next.js 13:** Opt for [`[email protected]`][app-router-branch-link], tailored for the App Router.
- **Next.js 12/13 Page Router:** Stick with [`[email protected]`][pages-router-branch-link].

Because `next-runtime-env` uses Next.js 14 caching, it is only compatible with Next.js 14 and up.
### 🔖 Version Guide

To use with Next.js 13 use [version 2.x][app-router-branch-link]. Version 2.x is build on top of server components it is only compatible with Next.js 13 App Router.
- **1.x:** Next.js 12/13 Page Router
- **2.x:** Next.js 13 App Router
- **3.x:** Next.js 14 with advanced caching

Use [version 1.x][pages-router-branch-link] for Next.js 12/13 Page Router support.
### 🚀 Getting Started

### Versions 🔖

- `[email protected]` - Next.js 12/13 Page Router support.
- `[email protected]` - Next.js 13 App Router support.
- `[email protected]` - Next.js 14 caching support.

### Getting started 🚀

Add the following lines to your `app/layout.tsx`:
In your `app/layout.tsx`, add:

```js
// app/layout.tsx
import { PublicEnvProvider } from 'next-runtime-env';
import { PublicEnvScript } from 'next-runtime-env';

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<PublicEnvScript />
</head>
<body>
<PublicEnvProvider>{children}</PublicEnvProvider>
{children}
</body>
</html>
);
}
```

The `PublicEnvProvider` will automatically expose all environment variables prefixed with `NEXT_PUBLIC_` to the context. If you want more control over which variables are exposed to the context, you can use the `EnvProvider` and define the exposed variables manually.
The `PublicEnvScript` component automatically exposes all environment variables prefixed with `NEXT_PUBLIC_` to the browser. For custom variable exposure, refer to [EXPOSING_CUSTOM_ENV.md](docs/EXPOSING_CUSTOM_ENV.md).

### Usage 🧑‍💻
### 🧑‍💻 Usage

In the browser your environment variables are now accessible using the `useEnvContext` hook. On the server you can use `process.env` because the layout is forced to be dynamic. For example:

```bash
# .env
NEXT_PUBLIC_FOO="foo"
BAR="bar"
```

> A `.env` file is not required, you can also declare your environment variables in whatever way you want.
Access your environment variables easily:

```tsx
// app/client-page.tsx
'use client';

import { useEnvContext } from 'next-runtime-env';

export default function SomePage() {
const { NEXT_PUBLIC_FOO } = useEnvContext();

return (
<main >
NEXT_PUBLIC_FOO: {NEXT_PUBLIC_FOO}
</main>
);
}
```

```tsx
// app/server-page.tsx
// This is as of Next.js 14, but you could also use other dynamic functions
import { unstable_noStore as noStore } from 'next/cache';
import { env } from 'next-runtime-env';

export default function SomePage() {
noStore(); // Opt into dynamic rendering

// This value will be evaluated at runtime
return (
<main >
BAR: {process.env.BAR}
</main>
);
const NEXT_PUBLIC_FOO = env('NEXT_PUBLIC_FOO');
return <main>NEXT_PUBLIC_FOO: {NEXT_PUBLIC_FOO}</main>;
}
```

### Utilities 🛠

We have included some utility function to make it even easier to work with
environment variables.

#### `makeEnvPublic(key: string | string[]): void`
### 🛠 Utilities

Makes an environment variable with a given key public. This is useful if you
want to use an environment variable in the browser, but it was was not declared
with a `NEXT_PUBLIC_` prefix.

For ease of use you can also make multiple env vars public at once by passing an
array of keys.

##### Example

```js
// next.config.js
const { makeEnvPublic } = require('next-runtime-env');

// Given that `FOO` is declared as a regular env var, not a public one. This
// will make it public and available as `NEXT_PUBLIC_FOO`.
makeEnvPublic('FOO');

// Or you can make multiple env vars public at once.
makeEnvPublic(['BAR', 'BAZ']);
```
Need to expose non-prefixed environment variables to the browser? Check out [MAKING_ENV_PUBLIC.md](docs/MAKING_ENV_PUBLIC.md).

> You can also use the experimental instrumentation hook introduced in Next.js 13. See the `with-app-router` example for more details.
### 👷 Maintenance

### Maintenance 👷
`next-runtime-env` is proudly maintained by [Expatfile.tax](expatfile-site), the leading US expat tax e-filing software.

This package is maintained and actively used by [Expatfile.tax][expatfile-site].
The #1 US expat tax e-filing software. 🇺🇸
### 📚 Acknowledgments

### Other work 📚
Kudos to the [react-env](react-env-repo) project for the inspiration, and a shoutout to @andonirdgz for the innovative context provider idea!

Big thanks to the [react-env][react-env-repo] project, which inspired us. 🙏
Also, a big shout out to @andonirdgz for the idea to use a context provider. 💪
---

[build-url]: https://img.shields.io/github/checks-status/expatfile/next-runtime-env/main
[cov-img]: https://codecov.io/gh/expatfile/next-runtime-env/branch/main/graph/badge.svg?token=mbGgsweFuP
Expand Down
50 changes: 49 additions & 1 deletion docs/EXPOSING_CUSTOM_ENV.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,56 @@
# Exposing custom environment variables 🛠

- [Exposing custom environment variables 🛠](#exposing-custom-environment-variables-)
- [Using the script approach (recommend)](#using-the-script-approach-recommend)
- [Example](#example)
- [Using the context approach](#using-the-context-approach)
- [Example](#example-1)

## Using the script approach (recommend)

You might not only want to expose environment variables that are prefixed with `NEXT_PUBLIC_`. In this case you can use the `EnvScript` to expose custom environment variables to the browser.

### Example

```tsx
// app/layout.tsx
// This is as of Next.js 14, but you could also use other dynamic functions
import { unstable_noStore as noStore } from 'next/cache';
import { EnvScript } from 'next-runtime-env';

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
noStore(); // Opt into dynamic rendering

// This value will be evaluated at runtime
return (
<html lang="en">
<head>
<EnvScript
env={{
NEXT_PUBLIC_: process.env.NEXT_PUBLIC_FOO,
BAR: process.env.BAR,
BAZ: process.env.BAZ,
notAnEnvVar: 'not-an-env-var',
}}
/>
</head>
<body>
{children}
</body>
</html>
);
}
```

## Using the context approach

You might not only want to expose environment variables that are prefixed with `NEXT_PUBLIC_`. In this case you can use the `EnvProvider` to expose custom environment variables to the context.

## Example
### Example

```tsx
// app/layout.tsx
Expand Down
63 changes: 63 additions & 0 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,68 @@
# Getting started 🚀

- [Getting started 🚀](#getting-started-)
- [Using the script approach (recommend)](#using-the-script-approach-recommend)
- [Using the context approach](#using-the-context-approach)

We recommend using the script approach, because you can use the environment variables outside the React context. If you intend to stay withing the react context you can use the context approach.

## Using the script approach (recommend)

1. First, install the package into your project:

```bash
npm install next-runtime-env
# or
yarn add next-runtime-env
# or
pnpm install next-runtime-env
```

1. Then add the script to your head in the root layout:

```tsx
// src/app/layout.tsx
import { PublicEnvScript } from 'next-runtime-env';

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
<PublicEnvScript />
</head>
<body>
{children}
</body>
</html>
);
}
```

1. Finally, use `env` utility to access the runtime environment variables any where in your app:

```tsx
import { env } from 'next-runtime-env';

export function MyComponent() {
const NEXT_PUBLIC_FOO = env('NEXT_PUBLIC_FOO');
const NEXT_PUBLIC_BAZ = env('NEXT_PUBLIC_BAZ');

useEffect(() => {
// some api call using NEXT_PUBLIC_BAZ
}, [NEXT_PUBLIC_BAZ]);

return <div>{NEXT_PUBLIC_FOO}</div>;
}
```

That's it! You can now use the next-runtime-env package to access runtime environment variables in your Next.js app.

## Using the context approach

1. First, install the package into your project:

```bash
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
"lint": "next lint"
},
"dependencies": {
"@types/node": "20.9.0",
"@types/node": "20.9.2",
"@types/react": "18.2.37",
"@types/react-dom": "18.2.15",
"eslint": "8.53.0",
"eslint-config-next": "14.0.1",
"next": "14.0.1",
"eslint": "8.54.0",
"eslint-config-next": "14.0.3",
"next": "14.0.3",
"next-runtime-env": "link:../..",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand Down
Loading

0 comments on commit 675d5f8

Please sign in to comment.