I need to create a custom font that applies to every Text component in the whole application.
Is there is a way to set a font globally in React Native?
One way is to create a wrapper for RN Text say MyTextCustomFont:
const MyTextCustomFont = (props) => {
return (
<Text style={{fontFamily:'myFont'}} {...props} >{props.children}</Text>
)
}
import this MyTextCustomFont and use anywhere.
Another way is to define a style object and use it wherever you want.
To do this we have to implement a method in which we will override Text component creation in React Native. In this we will set default font family or size or any attribute we want to set by default.
// typography.js
import React from 'react'
import { Text, Platform, StyleSheet } from 'react-native'
export const typography = () => {
const oldTextRender = Text.render
Text.render = function(...args) {
const origin = oldTextRender.call(this, ...args)
return React.cloneElement(origin, {
style: [styles.defaultText, origin.props.style],
})
}
}
const styles = StyleSheet.create({
defaultText: {
fontFamily: 'NunitoSans-Regular',//Default font family
}
});
Then in index.js you have to do this:
import { typography } from './src/utils/typography'
typography()
Detailed answer here:
https://ospfolio.com/two-way-to-change-default-font-family-in-react-native/
I think your problem is add Custom Fonts in react native.
1. Add Your Custom Fonts to Assets
Add all the font files you want to an “assets/fonts” folder in the root of your react native project:
2. Edit Package.json
Adding rnpm to package.json providing the path to the font files:
"rnpm": {
"assets": [
"./assets/fonts/"
]
},
3. Link assest files
run this command in your react native project root folder
react-native link
This should add the font references in your Info.plist file for iOS and on Android copy the font files to android/app/src/main/assets/fonts.
4. Add in stylesheet
Add a fontFamily property with your font name:
const styles = StyleSheet.create({
title: {
fontSize: 16,
fontFamily: 'PlayfairDisplay-Bold',
color: '#fff',
paddingRight: 20,
},
});
So, I've made a component doing this quite easely some times ago. This is working with Expo, I don't know for vanilla react-native.
at the start of your app:
import { Font, Asset } from 'expo'
async initFont() {
try {
await Font.loadAsync({
'Bariol': require('src/assets/font/Bariol_Regular.otf'),
'Bariol Bold': require('src/assets/font/Bariol_Bold.otf'),
})
this.setState({ fontReady: true })
} catch (e) {
console.log(e)
}
}
Then, you have to create a component file like text.js containing this code:
export default function (props) {
let font = { fontFamily: 'Bariol' }
if (props.bold) {
font = { fontFamily: 'Bariol Bold' }
}
const { bold, style, children, ...newProps } = props
return (
<Text {...newProps} style={[Style.text, props.style, font]}>
{props.children}
</Text>
)
}
Finally, in any of you other component / page just import MyText:
import Text from 'path/to/text.js'
use it like a normal Text component:
<Text bold>Hello World!</Text>
Even if this solution looks a bit more complicated than the others, it is easier to use once the setup is ok, since you just have to import Text.
You can override Text behaviour by adding this in any of your component using Text:
Edit: Add this code in your App.js or main file
let oldRender = Text.render;
Text.render = function (...args) {
let origin = oldRender.call(this, ...args);
return React.cloneElement(origin, {
style: [{color: 'red', fontFamily: 'Arial'}, origin.props.style]
});
}
For react Native Version 0.56 or below, Add this code in your App.js or main file
let oldRender = Text.prototype.render;
Text.prototype.render = function (...args) {
let origin = oldRender.call(this, ...args);
return React.cloneElement(origin, {
style: [{color: 'red', fontFamily: 'Arial'}, origin.props.style]
});
};
Reference
Or create your own component, such as MyAppText.
MyAppText would be a simple component that renders a Text component using your universal style and can pass through other props, etc.
I use a wrapper with default props like this :
const CustomText = ({ fontFam = "regular", ...props }) => {
const typo = {
light: "Montserrat_300Light",
regular: "Montserrat_400Regular",
bold: "Montserrat_600SemiBold",
};
return (
<Text {...props} style={[{ fontFamily: typo[fontFam], ...props.style }]}>
{props.children}
</Text>
);
};
export default CustomText;
By default, if "fontFam" is not indicated it will be regular font.
An example with bold typo :
<CustomText fontFam="bold" style={{ marginTop: 30, color: "grey" }}>
Some Text
</CustomText>
You can replace all your <Text/> by <CustomText />.
If you don't have to create custom component, you could try react-native-global-font. It will be apply for your all Text and TextInput
yes
app.js
import styles from './styles';
{...}
<Text style={styles.text}>hello World </Text>
{...}
styles.js
import {StyleSheet} from 'react-native';
const styles = StyleSheet.create({
text: {
// define your font or size or whatever you want to style here
},
use style on every text and all changes will affect all text components
Related
I'm trying to dynamically apply margin & padding to a View, based on a ref'd TextInput's borderRadius. I am new to React coming from Xamarin where this type of thing is common.
I'm not sure if I have the correct approach, but I have seen some examples of people deriving style values from useRef.
Here is my custom LabelInput component:
import React, {useState} from 'react';
import {
View,
Animated,
StyleSheet,
ViewProps,
} from 'react-native';
import {Colors} from '../../../resources/colors';
import Text from './Text';
import TextInput from './TextInput';
import {TextInputProps} from 'react-native/Libraries/Components/TextInput/TextInput';
import {isNullOrWhiteSpace} from '../../../utils/stringMethods';
import {TextProps} from 'react-native/Libraries/Text/Text';
interface LabeledInputProps {
label: string;
error: string;
onChangeText: (text: string) => void;
placeholder?: string;
inputValue: string;
mask?: (text: string) => string;
validator?: (text: string) => string;
onValidate?: (value: string) => void;
viewProps?: ViewProps;
textProps?: TextProps;
errorTextProps?: TextProps;
inputProps?: TextInputProps;
}
export default function LabeledInput(props: LabeledInputProps) {
const inputRef = React.useRef<any>(null);
const [dynamicStyle, setDynamicStyle] = useState(StyleSheet.create({
dynamicContainer:{
marginHorizonal: 0,
paddingHorizonal: 0,
}
}));
const changeTextHandler = (inputText: string) => {
const displayText = props?.mask ? props.mask(inputText) : inputText;
props.onChangeText(displayText);
// ultimately not the exact behavior I'm after, but this is a simple example.
var test = inputRef.current.props.style;
// props.style always returns undefined,
// there doesn't appear to be a 'props' property on the 'current' object when debugging.
setDynamicStyle(StyleSheet.create({
dynamicContainer:{
marginHorizonal: test.borderRadius, // I want the padding/margin of this element to be
paddingHorizonal: test.borderRadius,// dynamically set based on the inputRef's borderRadius
}
}))
};
return (
<View
{...props.viewProps}
style={[
props.viewProps?.style,
localStyles.container,
]}>
<TextInput
ref={inputRef}
{...props.inputProps}
placeholder={props.placeholder}
style={localStyles.input}
onChangeText={changeTextHandler}
value={props.inputValue}
/>
<Animated.View
pointerEvents={'none'}>
<Text
{...props.textProps}
style={[props.textProps?.style, animatedStyles.label]}>
{props.label}
</Text>
</Animated.View>
{/* {stuff} */}
</View>
);
}
const localStyles = StyleSheet.create({
container: {
backgroundColor: 'blue',
justifyContent: 'flex-start',
flex: 1,
},
label: {
fontWeight: 'bold',
marginBottom: 8,
},
input: {
padding: 8,
},
error: {
backgroundColor: 'pink',
fontSize: 12,
paddingHorizontal: 8,
color: Colors.danger,
marginTop: 4,
},
});
const animatedStyles = StyleSheet.create({
label: {
fontSize: 16,
fontWeight: 'normal',
},
});
Here is my custom LabelInput component with forwardRef() implemented:
import React, {ForwardedRef, forwardRef} from 'react';
import {TextInput as NativeTextInput, TextInputProps} from 'react-native';
import {useGlobalStyles} from '../../../resources/styles';
const TextInput = (
props: TextInputProps,
ref: ForwardedRef<NativeTextInput>,
) => {
const styles = useGlobalStyles();
return (
<NativeTextInput
{...props}
ref={ref}
style={[styles.textInput, props.style]}
placeholderTextColor={styles.textInput.borderColor}
onChangeText={(text: string) => {
if (props.onChangeText) {
props.onChangeText(text);
}
}}
/>
);
};
export default forwardRef(TextInput);
I've tried referencing inputRef from different hooks, like useCallback & useEffect.
var test = inputRef.current.props.style; always returns undefined. And there doesn't appear to be a 'props' property on the 'current' object when debugging.
The link you mentioned contains two files with inputRef. Since inputRef is in parent component and use ref prop to pass inputRef, this will not work. ref is not available as prop. If you still want to use ref as prop, then use forward ref in child component as access the ref as second argument or you can use any other prop name to pass ref i.e. innerRef. You can read more in react documentation. Forward Refs
According to the code you attach in code sandbox, i think you are trying to access input styles in two components: App and LabeledInput. You should use one ref in main component and use it in LabelInput component. If you still want to have separate refs then you can ref callback function and attach the node with both refs.
const attachRef = (node: NativeTextInput) => {
inputRef.current = node;
ref.current = node;
};
return <TextInput ref={attachRef} />;
The correct type for inputRef.current is TextInputProps.
const inputRef = useRef() as MutableRefObject<TextInputProps>;
I have updated the code sandbox. I was able to access input field styles in both components. Hope this solves your problem.
I have got back into React Native after a few years and wanted to get a custom font. I tried looking through the documentation but they only have a way how to do it on the page itself and you can't use styles. They mentioned
Font.LoadAsync()
But that too is not working for me as I am using a function rather than a class.
I looked into the code I did a few years back and found out that the way I did this was this:
import * as Font from "expo-font";
export default useFonts = async () =>
await Font.loadAsync({
'CantoraOne': require('../assets/fonts/CantoraOne-Regular.ttf'),
});
export default function Dice() {
const [IsReady, SetIsReady] = useState(false);
const LoadFonts = async () => {
await useFonts();
};
if (!IsReady) {
return (
<AppLoading
startAsync={LoadFonts}
onFinish={() => SetIsReady(true)}
onError={() => { }}
/>
);
}
return (
<Text style={styles.text}>Some text</Text>
)
}
const styles = StyleSheet.create({
text: {
color: '#fff',
fontFamily: 'CantoraOne',
fontSize: 50,
},
})
This worked but when I tried doing the same workaround now. I found out that "App Loading" is deprecated and should not be used. I don't know of any other way how to do it and I feel lost.
How can I make a central place from which I can import a custom font into every page on my project without needing to write "Font.LoadAsync()" into every page?
AppLoading is deprecated because the functionality is easily created on your own. Check out the usage recommended in the current docs:
https://docs.expo.dev/versions/latest/sdk/font/#usage
Keep loading state at the root level to track whether the fonts are loaded, starting with true
Load the fonts in a useEffect, set loading to false when done
Return a loading indicator while the fonts are unloaded and the app when they are done
Now I am trying to use FontAwesome in React Native App on Expo.
I followed the flow about how to use custom font through Expo on Expo document but these icons couldn't work and I couldn't see any fonts.
Fortunately I don't have any error but warning.
This is my code below.
Please teach me.
Thank you.
import { Font } from 'expo';
import { fontAwesome } from '../../assets/fonts/fa-solid-900.ttf';
class Help extends React.Component {
state = {
fontLoaded: false,
};
async componentDidMount() {
await Font.loadAsync({
FontAwesome: fontAwesome,
});
this.setState({ fontLoaded: true });
}
renderHelpLists() {
return HelpLists.map((value, index) => {
return (
<TouchableOpacity
key={index}
style={styles.helpListsBox}
>
<Text style={styles.helpListText}>
{value.name}
</Text>
{
this.state.fontLoaded ? (
<Text style={styles.rightIcon}>
{'\f054'}
</Text>
) : null
}
</TouchableOpacity>
);
});
}
const styles=Stylesheet.create({
rightIcon: {
fontFamily: 'FontAwesome',
},
})
I'm not yet familiar with using Font Awesome by importing TTF files in React Native.
However, could you try using the Font Awesome React Native component?
See https://github.com/fortawesome/react-native-fontawesome
I solved this problem with import Icon from 'react-native-vector-icons/FonteAwesome'.
However I am not sure how to use Expo-Icons.
how to pass form data from screen1 to screen2 in react native ? I have following code in scrren1 I want posted amount data in screen2. Please let me know how can I pass data on screen2 and receive it in react native?
import React, { Component } from 'react';
import { Button, View, Text, StyleSheet } from 'react-native';
import t from 'tcomb-form-native'; // 0.6.9
const Form = t.form.Form;
const User = t.struct({
amount: t.String,
});
const formStyles = {
...Form.stylesheet,
formGroup: {
normal: {
marginBottom: 10
},
},
controlLabel: {
normal: {
color: 'blue',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
},
// the style applied when a validation error occours
error: {
color: 'red',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
}
}
}
const options = {
fields: {
amount: {
label: "Enter Amount You want to Top up",
error: 'Please add amount to proceed ahead!'
},
},
stylesheet: formStyles,
};
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
handleSubmit = () => {
const value = this._form.getValue();
console.log('value: ', value);
}
render() {
return (
<View style={styles.container}>
<Form
ref={c => this._form = c}
type={User}
options={options}
/>
<Button
title="Pay Now"
onPress={this.handleSubmit}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20,
backgroundColor: '#ffffff',
},
});
export default HomeScreen;
It depends if you want to pass data between Parent to Child, Child to Parent or Between Siblings
I suggest you to read Passing Data Between React Components, old but this article did help me to understand the logic behind passing data as it's not as easy to implement as in other programming languages.
Excerpt using props:
class App extends React.Component {
render() {
[... somewhere in here I define a variable listName
which I think will be useful as data in my ToDoList component...]
return (
<div>
<InputBar/>
<ToDoList listNameFromParent={listName}/>
</div>
);
}
}
Now in the ToDoList component, use this.props.listNameFromParent to access that data.
You have many ways to send informations from one screen to another in React Native.
eg.
Use React Navigation to navigate between your scenes. You will be able to pass params to your components, which will be accessible in the navigation props when received.
this.props.navigation.navigate({routeName:'sceneOne', params:{name} });
You can also send directly props to a component, and treat them in it. In your render section of your first component, you could have something like this :
<myComponent oneProps={name}/>
In that example, you will receive the props "oneProps" in your second component and you will be able to access it that way :
type Props = {
oneProps: string,
}
class myComponent extends React.Component<Props> {
render() {
console.log('received sent props', oneProps);
return (
<View> // display it
<Text>{this.props.oneProps}</Text>
</View>
);
};
}
These are only two effective solutions, but there are a lot more.
Hope it helped you :)
Have a good day
I am trying to display an image in my app, using the documentation provided by React-Native. the code is here.
The only file that is concerned is the app.js file which has the code. the image is in the same folder as the app.js file. Can someone help me figure out why this might not be working?
the line of code in question is:
<Image source={require('./practicialogo.PNG')} />
I getting two errors:
1. Unable to resolve path to file (which i dont understand as the image is in the same folder as the file 2. ES Lint is giving me an unexpected require() (global require) error on that line where i call for the image.
Here is the entire file:
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
import { StackNavigator } from 'react-navigation';
import { Button } from 'react-native-elements';
//import { Button } from './src/components/Button';
//import { CardSection } from './src/components/CardSection';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'WELCOME!'
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Image source={require('./practicialogo.PNG')} />
<Text>Hello, Chat App!</Text>
<Button
raised
backgroundColor="#3399ff"
borderRadius="20"
onPress={() => navigate('Chat', { user: 'Lucy' })}
title="CHAT WITH LUCY"
/>
</View>
);
}
}
class ChatScreen extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: `Chat with ${navigation.state.params.user}`,
});
render() {
const { params } = this.props.navigation.state;
return (
<View>
<Text>Chat with {params.user}</Text>
</View>
);
}
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen }
});
export default class App extends React.Component {
render() {
return <SimpleApp />;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
}
});
My solution to the ESLint Error is to require images at the top of the file and then reference them in JSX like this:
const myImage = require('myImage.png');
<Image
source={myImage}
/>
I wouldn't concern yourself too much with the eslint error. Eslint isn't God, and some things you must do in RN are going to make it angry. As for the unresolved path, I know you already tried .png lowercase extension. Can you rename the image to a .png lowercase extension and then try that again? Your code should work, so that's all I can think of. Maybe throw it in an 'image' folder and require it from that path. I know it's essentially the same as what you already have, but sometimes just getting an image to show helps, and then work backwards from there.
You want your code to look like this:
source={require('./practicialogo.png')}
source={require('./image/practicialogo.png')} // or with it in an image folder
Extra curly braces and parenthesis might make the image unaccessible.
I hope that helps. Your code is correct, so that's all I can think of as to why it wouldn't find that image.
This is one of the rule from eslint to use require only on top as global reference
From eslint docs
In Node.js, module dependencies are included using the require()
function, such as:
var fs = require("fs");
While require() may be called anywhere in code, some style guides
prescribe that it should be called only in the top level of a module
to make it easier to identify dependencies. For instance, it’s
arguably harder to identify dependencies when they are deeply nested
inside of functions and other statements:
However, you can disable this rule for just that dependency using the // eslint-disable-line global-require comment. For eg if you want to disable it in jsx:
<View>
{/* eslint-disable-line global-require */}
<Image source={require('./practicialogo.PNG')} />
<Text>Hello, Chat App!</Text>
I tried requiring an image with uppercase extension and it's not working and throwing unable to resolve.
You may try to rename your practicialogo.PNG to practicialogo.png, then require('./practicialogo.png')