Can't seem to load custom fonts with Expo's Font.loadAsync - reactjs

I'm using React Native with Expo, and it is all going well except for this one issue with custom fonts. I have my font Lobster-Regular.ttfin ./assets/fonts, and I have been trying to load it as seen in the official docs:
componentDidMount() {
Font.loadAsync({
'Lobster': require('./assets/fonts/Lobster-Regular.ttf'),
});
}
I then style my header as such:
headerText: {
color: 'white',
fontSize: 30,
fontFamily: 'Lobster'
},
All I get is
fontFamily 'Lobster' is not a system font and has not been loaded
through Font.loadAsync.
If you intended to use a system font, make sure you typed the name correctly and that it is supported by your device operating system.
If this is a custom font, be sure to load it with Font.loadAsync.
Am I missing something?

Yes. You are missing that the call is Font.loadAsync(). This means that it loads asynchronously. As in: It takes a while. You can't render the UI until the font has loaded. You need to do something along these lines:
import { AppLoading, Font } from 'expo'
state = {
fontsLoaded: false,
...
}
componentWillMount() {
Font.loadAsync( {
'Lobster': require('./assets/fonts/Lobster-Regular.ttf')
}
).then( () => this.setState( { fontsLoaded: true } ) )
}
render() {
if( !this.state.fontsLoaded ) {
return <AppLoading/>
}
return (
...
)
}

Font.loadAsync is old and proved to have unpredictable problems. now expo have rolled out new solutions here
so the correct code is now:
import {useFonts} from 'expo-font';
import AppLoading from "expo-app-loading";
let [fontsLoaded] = useFonts({
'Lobster': require('./assets/fonts/Lobster-Regular.ttf'),
});
if (!fontsLoaded) {
return <AppLoading/>;
}
...

install expo-font package from expo CLI because sometime expo-font version is not compatible with your expo version so,
step 1:
expo install expo-font
step 2:
class App extends React.Component {
state = {
fontLoaded: false,
};
componentDidMount() {
this.loadAssetsAsync();
}
async loadAssetsAsync() {
await Font.loadAsync({
// Load a font `Montserrat` from a static resource
MuseoSans500: require("./assets/fonts/museosans_500-webfont.ttf"),
MuseoSans700: require("./assets/fonts/museosans_700-webfont.ttf"),
});
this.setState({ fontLoaded: true });
}
render() {
if (!this.state.fontLoaded) {
return null; // render some progress indicator
}
return <AnyComponent />;
}
}

** react-native font in stateless function **
**step:1 import font from expo **
import * as Font from 'expo-font';
import { AppLoading } from 'expo';
*step2: require font from files *
// fetchFonts from local files type ttf
const fetchFonts = () => {
return Font.loadAsync({
'PacificoRegular': require('../assets/Pacifico/Pacifico-Regular.ttf'),
});
};
*****step3:use state *****
// state font fetch control
const [fontloaded,setfontloaded]=useState(false);
**step4: use app loaded **
if(!fontloaded){
return(
<AppLoading
startAsync={fetchFonts}
onFinish={()=>{setfontloaded(true)}}
onError={console.warn}/>
)
}
**step5:style font **
txt:{
padding:5,
fontSize:18,
fontFamily: 'PacificoRegular',
}

useEffect(()=>{
async function loadFonts(){
await Font.loadAsync({
'Montserrat': require("./assets/fonts/Montserrat/Montserrat-Regular.ttf"),
'Montserrat-SemiBold': require('./assets/fonts/Montserrat/Montserrat-SemiBold.ttf'),
'Montserrat-Bold': require('./assets/fonts/Montserrat/Montserrat-Bold.ttf'),
'Fascinate': require('./assets/fonts/Fascinate/Fascinate-Regular.ttf')
}).then(res=>{
console.log("FONTS LOADED!");
setLoaded(true)
}).catch(Err=>{
setLoaded(true);
console.log(Err);
});
}
loadFonts();
},[])
Load using this useEffect in your App.js file, once loaded the fonts can be used anywhere in your expo or react-native project
const Heading = (color) => {
return({fontSize:45*fontScale, fontFamily: "Montserrat-SemiBold", color, marginTop: -10, marginLeft: InitialMargin, letterSpacing: 4})
}
Make sure you are not using the style fontWeight, as it will override the fontStyle and will not apply the fontFamily to your Text.

Because 'startAsync' is depreacted from the component AppLoading,
we can use coustom fonts as bellow,
This code works fine.
I think <Apploading> causes no further instructions to be
performed until the font is loaded
===============================================
import * as Font from 'expo-font';
import AppLoading from 'expo-app-loading';
import React ,{useState}from 'react';
export default function App() {
const [fontLoading,setFontLoading]=useState(false);
Font.loadAsync( {
yekan:require("./myapp/fonts/byekan.ttf"),
ih:require("./myapp/fonts/ih.ttf")
}
).then( () => setFontLoading('true') )
if (!fontLoading){
return(
<AppLoading/>);
} else{
return(
do someting.....
............
)
}

Related

fontFamily is not a system font and has not been loaded through Font.loadAsync But with Font.loadAsync

I'm learning react native by creating some projects.
I got stuck and unable to load custom fonts. I've followed this guide from expo docs. but I'm getting an error
fontFamily "raleway-bold" is not a system font and has not been loaded
through Font.loadAsync.
Then I have use this Font.loadAsync in my code but I still get the same error.
Any suggestions?
import React, { useState } from 'react';
import Home from './screens/Home';
import AppLoading from 'expo-app-loading';
import * as Font from 'expo-font';
const getFonts = () =>
Font.loadAsync({
'raleway-regular': require('./assets/fonts/Raleway-Regular.ttf'),
'raleway-bold': require('./assets/fonts/Raleway-Bold.ttf'),
});
export default function App() {
const [fontsLoaded, setFontsLoaded] = useState(false);
if (fontsLoaded) {
return <Home />;
} else {
return (
<AppLoading
startAsync={getFonts}
onFinish={() => setFontsLoaded(true)}
onError={console.warn}
/>
);
}
}
This is an expo client and AppLoading bug GitHub issue
You can try an alternative approach and not use AppLoading as below
useEffect(() => {
async function getFonts() {
await fetchFonts();
setFontLoaded(true);
}
getFonts();
}, []);
if (!fontLoaded) {
return (
<View>
<Text>Loading...</Text>
</View>
);
}

How to integrate Phaser into React

I've got a React application created with create-react-app and I'm trying to integrate Phaser 3 as well. I followed this guide to get started. I've got the canvas rendering the text but loading images in the preload does not seem to be working. I get the default failed to load texture image displayed.
import ExampleScene from "./scenes/ExampleScene";
import * as React from "react";
export default class Game extends React.Component {
componentDidMount() {
const config = {
type: Phaser.AUTO,
parent: "phaser-example",
width: 800,
height: 600,
scene: [ExampleScene]
};
new Phaser.Game(config);
}
shouldComponentUpdate() {
return false;
}
render() {
return <div id="phaser-game" />;
}
}
ExampleScene:
import Phaser from "phaser";
export default class ExampleScene extends Phaser.Scene {
preload() {
this.load.image("logo", "assets/logo.png");
}
create() {
const text = this.add.text(250, 250, "Phaser", {
backgroundColor: "white",
color: "blue",
fontSize: 48
});
text.setInteractive({ useHandCursor: true });
this.add.image(400, 300, "logo");
text.on("pointerup", () => {
console.log("Hello!");
//store.dispatch({ type: ACTION_TYPE });
});
}
}
The idea is to create a visualization with flowers growing based on a simple gene engine. So Phaser would get instructions from the Store about the current state.
I'm guess this has something to do with the way Phaser loads and there's a conflict with how React updates. I'm preventing the component from updating as I only need the game to receive instructions by listening to the store
I've already looked at this SO answer and the accompanying wrapper, but it is outdated.
How can I get Phaser to load images when in a Create-React-App?
CodeSandbox: https://codesandbox.io/s/github/nodes777/react-punnett/tree/phaser-game
Repo: https://github.com/nodes777/react-punnett/tree/phaser-game
Other option is using WebComponents to be able to integrate Phaser with any other framework (React, Angular, VueJS, etc), check this npm package: https://www.npmjs.com/package/#ion-phaser/core
Also, you can use the React wrapper of that library to use Phaser with React components easily, so you don't need to manipulate WebComponents directly, example:
import React from 'react'
import Phaser from 'phaser'
import { IonPhaser } from '#ion-phaser/react'
const game = {
width: "100%",
height: "100%",
type: Phaser.AUTO,
scene: {
init: function() {
this.cameras.main.setBackgroundColor('#24252A')
},
create: function() {
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY,
"Hello World", {
font: "40px Arial",
fill: "#ffffff"
}
);
this.helloWorld.setOrigin(0.5);
},
update: function() {
this.helloWorld.angle += 1;
}
}
}
const App = () => {
return (
<IonPhaser game={game} />
)
}
export default App;
Fore more details check the repo: https://github.com/proyecto26/ion-phaser/tree/master/react
A year ago I was here looking for the answer myself. Here's pattern which should work.
import Phaser from "phaser"
import React, { useEffect, useState } from "react"
/** #tutorial I made this! This answers how you get your image. */
import logoImage from "./path-to-logo.png"
/** #tutorial I made this! Use a functional React component and `useEffect` hook.*/
export const Phaser3GameComponent = ({ someState }) => {
// Optional: useful to delay appearance and avoid canvas flicker.
const [isReady, setReady] = useState(false)
// Just an example... do what you do here.
const dataService = (changedState) => {
// I'm not sure how to use stores, but you'll know better what to do here.
store.dispatch(
{
...someState,
...changedState,
},
{ type: ACTION_TYPE }
)
}
// This is where the fun starts.
useEffect(() => {
const config = {
callbacks: {
preBoot: game => {
// A good way to get data state into the game.
game.registry.merge(someState)
// This is a good way to catch when that data changes.
game.registry.events.on("changedata", (par, key, val, prevVal) => {
// Simply call whatever functions you want outside.
dataService({ [key]: val })
})
},
},
type: Phaser.AUTO,
parent: "phaser-example",
width: 800,
height: 600,
scene: [ExampleScene],
}
let game = new Phaser.Game(config)
// Triggered when game is fully READY.
game.events.on("READY", setReady)
// If you don't do this, you get duplicates of the canvas piling up.
return () => {
setReady(false)
game.destroy(true)
}
}, []) // Keep the empty array otherwise the game will restart on every render.
return (
<div id="phaser-example" className={isReady ? "visible" : "invisible"} />
)
}
export default class ExampleScene extends Phaser.Scene {
preload() {
this.load.image("logo", logoImage)
}
create() {
// You made this!
const text = this.add.text(250, 250, "Phaser")
text.setInteractive({ useHandCursor: true })
this.add.image(400, 300, "logo")
/** #tutorial I made this! */
// Get all that lovely dataState into your scene,
let { clickCount } = this.registry.getAll()
text.on("pointerup", () => {
// This will trigger the "changedata" event handled by the component.
this.registry.merge({ clickCount: clickCount++ })
})
// This will trigger the scene as now being ready.
this.game.events.emit("READY", true)
}
}
I started from scratch and created my own boilerplate from the phaser 3 template. I wrote about the specific steps to add React to the Phaser 3 template here.
It seems like you could eject from Create-React-App and add in Phaser 3 from there, but the warnings not to eject turned me away from that solution.
In my case I use the following component and it works fine:
import Phaser from 'phaser';
import * as React from 'react';
import { HTML_DIV_ID, gameConfig } from './gameConfig';
export const GameWrapper = () => {
const [game, setGame] = React.useState<Phaser.Game>();
React.useEffect(() => {
const _game = new Phaser.Game(gameConfig());
setGame(_game);
return (): void => {
_game.destroy(true);
setGame(undefined);
};
}, []);
return (
<>
<div id={HTML_DIV_ID} />
</>
);
};
With create-react-app and React.StrictMode:
Also I deleted React.StrictMode (default option with create-react-app) because it mounts
and unmounts all components so I had unexpected behavior with phaser
sometimes
You can use react hook for the code above as:
// usePhaser.js
export function userPhaser(config) {
const [game, setGame] = React.useState();
React.useEffect(() => {
const _game = new Phaser.Game(config);
setGame(_game);
return (): void => {
_game.destroy(true);
setGame(undefined);
};
}, []);
return game;
}
You need to put images inside the folder public!
For me, I see the best practice to use both of them properly is to create phaser project separately and host it separately using firebase or whatever hosting service you prefer, and then take the link and put it in an iframe tag inside react.
in this way you can manage them efficiently and you can manipulate react website in more comfortable way especially the mobile width compatibility.

react-codemirror2 has no CSS effect

I added react-codemirror2 to my project but it does not load the css although I import the codemirror.css file, because it is mentioned that css should be applied into the component somehow (mentioned here), but long story short it is still rendered like this:
Code Mirror with no CSS
I really don't know what the issue can be. So here is my code:
import { connect } from 'react-redux';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
import { Controlled as CodeMirror } from 'react-codemirror2';
require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript');
class MyComponent extends Component {
handleValueChange = value => this.props.onUpdateValue({ value });
render() {
const { shade } = this.props;
const myOptions = {
mode: 'xml',
theme: shade === 'dark' ? 'material' : 'default',
lineNumbers: true,
}
return (
<CodeMirror
id="editor"
value={this.props.value}
options={myOptions}
onBeforeChange={(editor, data, value) => {
this.handleValueChange(value);
}}
onChange={(editor, data, value) => {}}
/>
);
}
}
function mapStateToProps(state) {
return {
shade: state.muiTheme.shade,
};
}
export default connect(
mapStateToProps,
null
)(MyComponent);
I also tried to #import the css file inside the global.css file of my project (like below) but nothing's changed.
#import '/node_modules/codemirror/lib/codemirror.css';
#import '/node_modules/codemirror/theme/material.css';
I really don't know what else should be tried or what am I doing wrong, because it shouldn't be something very special. So I'm asking you, and any suggestions would be helpful.
Thanks :)
I don't know why you met the problem, but it works for me.
import React, { Component } from "react";
// Import the code mirror component.
import { Controlled as CodeMirror } from "react-codemirror2";
// The following two imports is for the theme.
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
// This import is for the language syntax highlighting.
import 'codemirror/mode/javascript/javascript.js';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
src: ''
}
}
render() {
let option = {
mode: 'javascript',
theme: 'material'
};
return (
<div>
<Controlled
value={this.state.src}
option={option}
onBeforeChange={(editor, data, value) => {
this.setState({ src: value });
}}
onChange={(editor, data, value) => {}}
/>
</div>
)
}
}
Maybe the problem is here:
const myOptions = {
mode: 'xml',
theme: shade === 'dark' ? 'material' : 'default',
lineNumbers: true,
}
You should set the option object in rendering function before return, or set it in constructor() and attach it to this, like this.option = {}.
Okay this is something which may only happened to me but I post my solution, maybe someday someone have same issue.
As I said the problem was not loading the css, I don't know how others handle this issue but I have to copy all styles inside the node_modules/codemirror/lib/codemirror.css into a new css file and put it in some path inside my project. And inside the global.css file of my project I just imported that new created file like #import absolute/path/to/file/codemirror.css; and it worked at least for one case and one theme. I'm sure there is better ways to connect to all css files of codemirror but for now it did the basic thing that I needed.

Loading font native-base error

I'm getting the error: You started loading 'Roboto_medium', but used it before it finished loading when using native base.
I've followed the instructions in the official page.
To create react native app I'm using create-react-native-app.
App.js
export default class App extends React.Component {
async componentWillMount() {
await Expo.Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
'Ionicons': require('#expo/vector-icons/fonts/Ionicons.ttf'),
});
}
render() {
return (
<Container>
<StatusBar hidden={true} />
<Button>
<Text>
Button
</Text>
</Button>
<ListaItens />
</Container>
);
}
}
you need to wait till the fonts get loaded. You can do something like this
import React from "react";
import { StatusBar } from "react-native";
import { Container, Button, text, ListItem, Text } from "native-base";
import Expo from "expo";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true };
}
async componentWillMount() {
await Expo.Font.loadAsync({
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
Ionicons: require("#expo/vector-icons/fonts/Ionicons.ttf"),
});
this.setState({ loading: false });
}
render() {
if (this.state.loading) {
return <Expo.AppLoading />;
}
return (
<Container>
<StatusBar hidden={true} />
<Button>
<Text>Button</Text>
</Button>
<ListItem />
</Container>
);
}
}
This new code for expo SDK 35 which was modified from #akhil xavier 's answer
First install expo-font
expo install 'expo-font'
here is the App.js
import React from "react";
import * as Font from "expo-font";
import { ActivityIndicator } from "react-native";
import { Root } from "native-base";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true };
}
async componentWillMount() {
await Font.loadAsync({
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
Ionicons: require("#expo/vector-icons/build/vendor/react-native-vector-icons/Fonts/Ionicons.ttf"),
});
this.setState({ loading: false });
}
render() {
if (this.state.loading) {
return <ActivityIndicator />;
}
return (
<Root>
<RootPage /> // starter component (i.e. nav)
</Root>
);
}
}
The solution that works for me is below. The error I had was that I imported Font as (Fonts) but while calling it failed to notice the 's'. Fixed it by making sure the import name is similar to what you call the loadAsync to. You need to have 'expo-font' installed in your project for it to work
import React from "react";
import * as Font from "expo-font";
import { AppLoading } from "expo";
import MealsNavigator from "./navigation/MealsNavigator";
const fetchFonts = () => {
return Font.loadAsync({
"open-sans": require("./assets/fonts/OpenSans-Regular.ttf"),
"open-sans-bold": require("./assets/fonts/OpenSans-Bold.ttf")
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => setFontLoaded(true)}
onError={err => console.log(err)}
/>
);
}
return <MealsNavigator />;
}
If anyone is having this issue with the 'MaterialIcons' font family, I had a similar problem and found following this solution worked:
https://javascriptrambling.blogspot.com.au/2018/03/expo-icon-fonts-with-react-native-and.html
You basically need to:
Install the fonts (using npm install)
Do a Font.loadAsync on the fonts in your componentWillMount() function.
Remember to mark the componentWillMount() function as async
The conditionally display either as 'loading' or with the view depending on the state of a 'loaded' flag.
For example:
import React from 'react';
import { View } from 'react-native';
import { Avatar } from 'react-native-elements';
import { AppLoading, Font } from 'expo';
import FontAwesome
from './node_modules/#expo/vector-icons/fonts/FontAwesome.ttf';
import MaterialIcons
from './node_modules/#expo/vector-icons/fonts/MaterialIcons.ttf';
export default class App extends React.Component {
state = {
fontLoaded: false
};
async componentWillMount() {
try {
await Font.loadAsync({
FontAwesome,
MaterialIcons
});
this.setState({ fontLoaded: true });
} catch (error) {
console.log('error loading icon fonts', error);
}
}
render() {
if (!this.state.fontLoaded) {
return <AppLoading />;
}
return (
<View>
<Text>My App</Text>
<Avatar
small
rounded
icon={{ name: 'add' }}
/>
</View>
);
}
}
This is the way which I use to load Fonts in my project. I feel this more abstracted as compared to all the other answers.
What you need to do is create a custom hook to Load fonts
You can do it something like this
Create a folder called hooks where your App.js is located. Then inside hooks folder create a file called useFonts.js
Inside useFonts.js write like this
import * as Font from 'expo-font';
export default useFonts = async () =>
await Font.loadAsync({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
Ionicons: require('#expo/vector-icons/fonts/Ionicons.ttf'),
});
Now in your App.js write like this
import * as Font from 'expo-font';
import AppLoading from 'expo-app-loading';
import React, { useState } from 'react';
import useFonts from './hooks/useFonts';
export default function App() {
const [IsReady, SetIsReady] = useState(false);
// This function will start the fontLoading
const LoadFonts = async () => {
await useFonts();
};
// This is a check to ensure fonts get loaded
if (!IsReady) {
return (
<AppLoading
startAsync={LoadFonts}
onFinish={() => SetIsReady(true)}
onError={() => {}}
/>
);
}
// If fonts are successfully loaded then show the rest of your App
return (
<Container>
<StatusBar hidden={true} />
<Button>
<Text>Button</Text>
</Button>
<ListaItens />
</Container>
);
}
One reason why it has to load font is because you are using the Native Base Text component. If you import the React Native Text component instead you won't even have to load fonts and you won't see that error.

Webpack theme loader

I'd like to accomplish the following structure:
button.core.jsx
button.theme-a.jsx
button.theme-b.jsx
To take React as an example, I'd like to do the following in button.core.jsx:
import React from 'react';
import Themed from './button.[theme]';
export default class Button extends React.Component {
render() {
if (Themed) {
return <Themed />;
}
return <button>default button</button>;
}
}
In other words, I want to define a theme in my webpack.config.js and load that file if it exists. If it does't, render the default behaviour. I think this would be a very powerful setup!
I've been searching around for making a custom loader, but no success yet. Can anyone point me in the right direction?
I've got this working with writing a custom "resolver":
const ThemeResolver = {
apply: function(resolver) {
resolver.plugin('file', function(req, cb) {
if (req.request.indexOf('[theme]') == -1) {
return cb();
}
const defaultFile = req.request.replace('[theme]', 'Default');
const themedFile = req.request.replace('[theme]', process.env.THEME);
req.request = themedFile;
this.doResolve(['file'], req, (err) => {
if (!err) {
return cb();
}
req.request = defaultFile;
this.doResolve(['file'], req, cb);
})
});
}
};
module.exports = {
// ...
plugins: [
new webpack.ResolverPlugin([ThemeResolver]),
]
// ...
};
It tries to resolve a file with [theme] in its path into a path with the theme defined as a environment variable. If it fails, it'll fallback to a default file instead. This way I can require a themed file like so:
import Presentation from './button-[theme]'
The main component turned out to be a bit different than in my question, but I'm actually pretty content with it:
import React from 'react';
import Presentation from './button-[theme]';
export default class Button extends React.Component {
onClick = (e) => console.log('some logic');
render() {
return <Presentation onClick={ this.onClick } />;
}
}
The logic of this button-component can live inside of button.core.jsx, while the presentation will be handled by one of these components:
THEME=theme-a npm start // button-[theme] resolves to button-theme-a.jsx
THEME=theme-c npm start // button-[theme] resolves to button-default.jsx
Disclaimer: I didn't use this in a large scale or production environment yet, but it seems to work in a small POC. Please let me know if I'm doing something unwise!

Resources