Package: django-cra-helper
django-cra-helperIntroductionInstallationConfiguration1. settings.py2. urls.py3. asset-manifest.jsonDevelopmentProductionSupporting CRA's relative pathsReact in Django templatesSpecifying React Components via template contextCombining Django and React routesReferencing React static filesThe Payoff RevealedIntroductiondjango-cra-helperis the missing link betweenDjangoandcreate-react-app. By adding this to your Django project, you can almost effortlessly inject your React components into your Django templates and initialize component props via Django context variables.The ultimate goal of this package is to integrate these two projects with minimal changes to workflows that are typically used with either during development. Fromnpm starttopython manage.py collectstatic, your commands should work as expected so you can forget about implementation and get back to development!Note: For the purposes of this README, the abbreviationCRAwill be used to refer tocreate-react-app.InstallationThis package is available for installation viapip:pipinstalldjango-cra-helperConfiguration1. settings.pyOncedjango-cra-helperis installed,cra_helperwill need to be added toINSTALLED_APPSinsettings.py:INSTALLED_APPS=['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','cra_helper','django.contrib.staticfiles',]Note:cra_helpermustbe placed abovedjango.contrib.staticfilesin the list!Addcra_helper.context_processors.statictoTEMPLATES['OPTIONS']['context_processors']:TEMPLATES=[{# ...snip...'OPTIONS':{'context_processors':[# ...snip...'cra_helper.context_processors.static',],},},]Additionally, the followingSTATICFILES_FINDERSlist must be added tosettings.py:STATICFILES_FINDERS=[# Required for CRAManifestFinder below to work'django.contrib.staticfiles.finders.FileSystemFinder',# A finder to pull in asset-manifest.json'cra_helper.finders.CRAManifestFinder',]The last necessary setting is the name of the folder containing the CRA project files, relative to the base directory of the Djangoproject(the folder containingmanage.py):CRA_APP_NAME='cra-app'If for some reason the CRA liveserver doesnotserve on localhost or port 3000, the following settings can be added tosettings.pyto specify its actual host and port:CRA_HOST='0.0.0.0'# defaults to 'localhost'CRA_PORT=9999# defaults to 30002. urls.pyHot-reloading support can be enabled by first adding the following to your project or app'surls.pyfile:# import Django settingsfromdjango.confimportsettings# add `re_path` import herefromdjango.urlsimportpath,re_path# ...other imports...fromcra_helper.viewsimportproxy_cra_requests# other existing urlsurlpatterns=[...]# add a reverse-proxy view to help React in the Django view talk to Create-React-Appifsettings.DEBUG:proxy_urls=[re_path(r'^__webpack_dev_server__/(?P<path>.*)$',proxy_cra_requests),re_path(r'^(?P<path>.+\.hot-update\.(js|json|js\.map))$',proxy_cra_requests),]urlpatterns.extend(proxy_urls)Next, follow the instructions below that correspond to your project's version ofreact-scripts:For projects usingreact-scripts@<3.3.0Add one more url toproxy_urlsabove:proxy_urls=[# ...snip...re_path(r'^sockjs-node/(?P<path>.*)$',proxy_cra_requests),]For projects usingreact-scripts@>=3.3.0Create an.envfile in the root of your Create-React-App folder with the following environment variable:WDS_SOCKET_PORT=30003. asset-manifest.jsonFinally, run CRA'snpm run buildcommand once to generate abuild/directory.django-cra-helperrequires thebuild/asset-manifest.jsonfile contained within to help load non-JS and non-CSS assets that might be used in any React components. This command should be re-run any time a new non-JS or non-CSS asset is added to the project.DevelopmentIf the CRA project's liveserver is started vianpm startprior to starting Django's development server viapython manage.py runserver, code changes in the React codebase will be updated immediately within Django views as well.When the CRA liveserver is running,django-cra-helperadds abundle_jsarray template variable that can be inserted into the Django view's template to load the liveserver's various files containing all of the current JS and CSS. These files are recompiled on-the-fly by the liveserver whenever edits are made to the React code. This file can be added to a Django template as follows:{% if bundle_js %}
  {% for file_url in bundle_js %}<scripttype="text/javascript"src="{{ file_url }}"></script>{% endfor %}
{% endif %}Note: Don't use thestatictemplate tag here! This file needs to be loaded from the CRA liveserver instead.Productiondjango-cra-helperalso takes care of ensuring that Django'scollectstaticcommand pulls in production-ready bundles built by CRA'snpm run buildcommand.First, prepare React files for production with the typical CRAnpmbuild command:npmrunbuildThis will output bundled, minified JavaScript and CSS, and assets to the/build/folder within the CRA project folder.Once this command is complete, run the following Django command to gather static files, including the compiled React assets:pythonmanage.pycollectstatic--no-inputReact assets will be included with the other static assets in thesettings.STATIC_ROOTdirectory, to be served as is usual in a Django production environment. Anasset-manifest.jsonfile will also get pulled in. The contents of this CRA-generated file are required bydjango-cra-helperto help reference React files that have had a unique hash added to their filenames during the build process.Similar to thebundle_jstemplate variable mentioned earlier,django-cra-helperincludes numerous other template variables when the CRA liveserver isnotrunning:For projects usingreact-scripts@>=3.2.0Starting withreact-scripts@3.2.0, a newentrypointsproperty can be found inasset-manifest.json. This contains an array of files thatdjango-cra-helpermakes available in templates to more easily inject these files via newentrypoints.cssandentrypoints.jsarrays:{% for file in entrypoints.css %}<linkhref="{% static file %}"rel="stylesheet">{% endfor %}{% for file in entrypoints.js %}<scripttype="text/javascript"src="{% static file %}"></script>{% endfor %}NOTE: These JavaScript and CSS files should be arranged in an order required for the site to load; the ultimate order is derived from the order present inasset-manifest.json.For projects usingreact-scripts@>=3.0.0 to react-scripts@<3.2.0Code-splitting was introduced in later versions ofreact-scriptsthat split upmain_jsinto multiple files. Additional<script>tags are required to enable the React project to load. Depending on the size of your project, in addition tomain_jsmentioned above you'll need to add at least two more in a specific order:{% if main_js %}<scripttype="text/javascript"src="{% static runtime_main_js %}"></script><scripttype="text/javascript"src="{% static static_js_2_9a95e042_chunk_js %}"></script><scripttype="text/javascript"src="{% static main_js %}"></script>{% endif %}The naming ofstatic_js_2_9a95e042_chunk_jsabove will differ from project to project. Unfortunately you'll have to manually confirm this value in your project'sasset-manifest.jsonand update accordingly. It doesn't seem to change between builds, though, so it may not be a value you need to regularly update...For older projects usingreact-scripts@<=2.1.8The two most important variables aremain_jsandmain_css. These can be injected into the page via a typical call to{% static %}in the template:{% if main_css %}<linkhref="{% static main_css %}"rel="stylesheet">{% endif %}{% if main_js %}<scripttype="text/javascript"src="{% static main_js %}"></script>{% endif %}NOTE: Recent attempts at building a fresh CRA project withreact-scripts@2.1.8were unsuccessful in recreating SPAs that allowed for just a singlemain_js.npm run build-produced artifacts functioned almost identically to artifacts generated the same asreact-scripts@3.1.2, detailed below.There may be child dependencies ofreact-scriptsthat make it no longer possible to start apps that will function with the above instructions. In these cases, please try the instructions in the next section.Supporting CRA's relative pathsCRA allows developers to specify arelative sub-folder for their site to be hosted fromvia the"homepage"property inpackage.json:{"name":"cra-app","version":"0.1.0","homepage":"/frontend",...}When this value is set,npm run buildwill output assets and anasset-manifest.jsonwith paths prepended with the path prefix:Before: /static/js/main.319f1c51.chunk.js
After:  /frontend/static/js/main.319f1c51.chunk.jsTo make sure the React imports/assets/etc... can be found even when hosted through Django, you'll also need to updateSTATIC_URLin Django's settings.py to include the path prefix:STATIC_URL='/frontend/static/'CRA_PACKAGE_JSON_HOMEPAGE='/frontend'The value set toCRA_PACKAGE_JSON_HOMEPAGEabove should match the value of"homepage"inpackage.jsonso thatdjango-cra-helpercan find the CRA liveserver and redirect appropriately:Once these changes are made then the React app should be able to find everything it needs to function.React in Django templatesSpecifying React Components via template contextThe CRA project will need to undergo a small bit of re-architecture to prepare it to accept input values via context when Django serves the view. The following is an example of how a couple of small tweaks to a CRA project'ssrc/index.jsfile will establish a simple API for Django to communicate with the bundled React codebase:importReactfrom'react';importReactDOMfrom'react-dom';importAppfrom'./App';import'./index.css';/*** Maintain a simple map of React components to make it easier* for Django to reference individual components*/constpages={App,}/*** If Django hasn't injected these properties into the HTML* template that's loading this script then we're viewing it* via the create-react-app liveserver*/window.component=window.component||'App';window.props=window.props||{env:'Create-React-App'};window.reactRoot=window.reactRoot||document.getElementById('root');/*** React the component as usual*/ReactDOM.render(React.createElement(pages[window.component],window.props),window.reactRoot,);Basically,index.jsis updated to read values set towindow.component,window.props, andwindow.reactRootand use these to render a component. Each of these three "inputs" will allow Django to easily specify which component to initialize on a per-view basis:window.component: Astringthat points to a Component entry inpageswindow.props: AnObjectcontaining props to get passed into the Componentwindow.reactRoot: aninstanceofdocument.getElementByIdNote: Settings these values is optional. The defaults specified in the template above enable components to render as expected when viewed from the CRA liveserver.Now that the "API" is in place, Django Views can include values for these inputs via the context they pass to their template:defindex(request):context={'component':'App','props':{'env':'Django',},}returnrender(request,'index.html',context)Below is the Django app view'sindex.htmltemplate that can render across multiple versions ofreact-scripts(intended only for demo purposes with a fresh CRA app):{% load static %}<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">{% if entrypoints %}
      {% for file in entrypoints.css %}<linkhref="{% static file %}"rel="stylesheet">{% endfor %}
    {% elif main_css %}<linkhref="{% static main_css %}"rel="stylesheet">{% endif %}<title>Django + React Project</title></head><body><divid="react">Loading...</div>{{ props | json_script:"react-props" }}<script>window.component='{{ component }}';window.props=JSON.parse(document.getElementById('react-props').textContent);window.reactRoot=document.getElementById('react');</script>{% if bundle_js %}
      {% for file in bundle_js %}<scripttype="text/javascript"src="{{ file }}"></script>{% endfor %}
    {% elif entrypoints %}
      {% for file in entrypoints.js %}<scripttype="text/javascript"src="{% static file %}"></script>{% endfor %}
    {% elif main_js %}<scripttype="text/javascript"src="{% static runtime_main_js %}"></script>{# make sure to update this accordingly according to asset-manifest.json #}<scripttype="text/javascript"src="{% static static_js_2_597565cd_chunk_js %}"></script><scripttype="text/javascript"src="{% static main_js %}"></script>{% endif %}</body></html>The context'scomponentandpropsare bound towindow.componentandwindow.propsrespectively.Note the use of thejson_scriptfilter when settingwindows.props.Django provides this filteras a way to easily sanitize and convert a Pythondictto a JavascriptObject. The contents of the injected<script>tag can be run throughJSON.parse()to safely assign it to a variable.Finally,window.reactRootspecifies the container element that the React component should be rendered into. Setting a value for this is only required if the container'sidisnot"root"(the same ID assigned to the container<div>in the CRA project'sindex.html.)Combining Django and React routesIn some scenarios it may be desirable to leverage React client-side routes alongside typical Django routes. Fortunately it's simple to define an additional "catch-all" Django route that will gracefully handle URLs that should be handled by routing in the React app.In a typical client-side routing setup a React app may have the following routes defined:import{BrowserRouterasRouter,Switch,Route,NavLink,Redirect}from'react-router-dom';constApp=()=>(<Router><ul><li><NavLinkto='/'>ToHome</NavLink></li><li><NavLinkto='/foo'>ToFoo</NavLink></li><li><NavLinkto='/bar'>ToBar</NavLink></li></ul><Switch><Routepath='/foo'>foo</Route><Routepath='/bar'>bar</Route><Routeexactpath='/'>home</Route><Routepath='*'><Redirectto='/'/></Route></Switch></Router>);On the Django side of things, the following routes can be defined in afrontendapp'sfrontend/urls.pythat both point to anindexview that rendersfrontend/templates/index.html:fromdjango.urlsimportpath,re_path,includefrom.viewsimportindexurlpatterns=[# Default index template to render index.htmlpath('',index),# Helps Django pass unknown routes to the client-side routerre_path(r'^.*/$',index)]These URLs can then beinclude'd in the project's rooturls.py:fromdjango.urlsimportpath,includeurlpatterns=[# ...other routes...path('',include('frontend.urls')),]Note: If your routes aren't working, make sure'frontend'has been added toINSTALLED_APPSinsettings.pyWith both paths in place Django will now load the React app when it attempts to handle a path that is only defined as a client-side route.Referencing React static filesOther assets bundled by CRA, including image assets, can be accessed in templates by substituting/,.,~, and-in their filepaths with_.django-cra-helperadds every entry inasset-manifest.jsonto the base context, using these substitution rules to accomodate Django'sstatictag.For example, alogo.svgfile in the CRA project can be included in a Django template as follows:<!-- This file is located at `/build/static/media/logo.svg` --><imgsrc="{% static static_media_logo_svg %}"height="40"width="40">Note: This is optional! Static assets can still be included in the Django app's/static/directory and loaded as usual. The special substitution mentioned above is only needed when reusing React assets outside of specific components.The Payoff RevealedWhen all is said and done, React components should now render and be viewable in both the CRA liveserver and when served via Django. Here's an example of a slightly-modified CRAAppcomponent displayed in CRA (left) and Django (right):