I've built a reactjs site and trying to get it to be deployable.
right now all configuration is done via a config.js which is imported into all modules.
But when I build the app, this gets compiled into the deployment js and isn't configurable.
I want to have a separate file which a sys admin can configure various settings specific to their enviroment, such as the API end points (the app may not be running on the same server as the backend, and there will not be any access to DNS).
Is there a way to do this in react? I do not wish to use 3rd party libraries for this either.
You can store the configuration data in a JSON file. Just like a component, the JSON file can be loaded using the import statement. And then you can use the data as any other JavaScript object. For example, configData.
env file in our react js project. If you are using some external APIs for data you must use the . env file to store your sensitive credentials like API keys. Environment variables can also help us to store our API link in one location so that we don't have to change the link in each file manually.
Not sure about the linux approach, but I am using create-react-app (cra), Docker & kubernetes a lot and initially struggled with similar problem. I was then inspired by https://github.com/inloop/cra-docker and was able to find a solution for the configuration file problem with create-react-app at runtime in both Docker and kubernetes. Below are the steps in my solution:
Have your custom configuration (eg: config.js
) ready. The contents inside your config file should look like this:
window.ENV = {
"ENVIRONMENT":"stg",
...other key-value configuration as you'll need
}
Your configurations will be accessible anywhere in your code by accessing window.ENV.your_configuration_key
(eg: the ENVIRONMENT value above is available at window.ENV.ENVIRONMENT
)
Under public
directory, edit the index.html and add
<script type="text/javascript" src="%PUBLIC_URL%/config.js"></script>
in your head before the body. And put config.js
under public
directory.
Your goal in solving the external configuration for cra is that you want to put your config.js
file outside source directory and put it under the static public
directory. If you put the config under source directory, the config will get compiled during build time so you won't able to change the config during runtime easily (well, templating works too but it's not ideal to me). Do note that serving files from static directory require a server, but since I'm already using nginx
anyway to serve my static react app, I have absolutely no trouble doing this.
Since I was already using nginx
to serve the static files for my react app, I don't need to make changes to my Dockerfile to cater for additional config.js
as it will be available under the build
directory after compiled (due to it being placed under public
directory). My Dockerfile looks something like this:
# Step 1: Build static react app
FROM node AS builder
# Define working directory and copy source
WORKDIR /app
COPY . .
# Install dependencies and build whatever you have to build
RUN yarn install && yarn build
# Step 2: Run image
FROM nginx
COPY --from=builder /app/build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx # this step is not required, only when you have custom nginx configuration
Then afterwards build your docker image:docker build -t [your-docker-image]:latest .
Last but definitely not least, you'll want to replace your config.js
file during runtime. This can now be done easily.
If you're running using docker, you can replace files by using the -v command. So your docker runtime command should look something similar to this:
docker run --name [app-container-name] -d -p [host_port]:80 \
-v [path_to_config.js_file_in_certain_environment]:/usr/share/nginx/html/config.js \
[your-docker-image]
If you're running using kubernetes, you can replace files in an existing directory under your container by using configMap
, volume
, volumeMounts
and subPath
.
First put your config.js under k8s ConfigMap:
kubectl create configmap [k8s_config_name] --from-file=config.js=[path_to_config.js_file_in_certain_environment]
Mount your configMap in your k8s deployment:
containers:
...
volumeMounts:
- name: [k8s_config_name_volume]
readOnly: true
mountPath: "/usr/share/nginx/html/config.js"
subPath: "config.js"
volumes:
- name: [k8s_config_name_volume]
configMap:
name: [k8s_config_name]
Note that both mountPath
and subPath
parameters are necessary to replace a file under a directory that already has some files existing in it. If subPath
is omitted during volume mount to an existing directory which already contains some files the result is unfavourable in our case cos it will override the existing directory by copying the new file into the directory but removing all other previously existing files.
I've managed to hack together a solution.
in the public folder 'config.js'
var config = {
x: 'y',
};
Next wrap the ReactDOM.render (App/index.js in a fucntion like so
window.RenderApp = (config) => {
ReactDOM.render(<App _config={config}/>, document.getElementById('root'));
}
In the index.html add these lines, the window.RenderApp HAS to be at the end, because it relies on bundle.js being imported which is auto added by react and has a random name in production.
</html>
...
<head>
...
<script type="text/javascript" src="%PUBLIC_URL%/config.js"></script>
...
</head>
...
<body>
...
</body>
<script>
window.RenderApp(config);
</script>
</html>
lastly to use the config variables in your App.js or what ever you called it
...
constructor(props) {
super(props)
console.log(this.props._config)
this.state = {
....
config: this.props._config,
}
}
...
I found you have to set config to a state variables or else it will randomly throw undefined for the object, now just pass config down the hierarchy to use in your code.
We had a similar use case to - 'build once, deploy anywhere'. The following code seems to work for us:
There is config.js
file in the public
directory with the following code:
const API_URL = '<API_URL>';
index.html
file contains the following:
<script src="/config.js"></script>
<script>
(function (global) {
var config = {
API_URL
};
global.app = {
env: config
};
})(window);
This adds the variables from config.js
into the global
scope and can be accessed anywhere in the source code as : const API_ROOT = window.app && window.app.env.API_URL;
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With