Typescript support for Themes
Current Behavior
I am adding extra colors to the Theme but Typescript complains that they don't exist when using the useTheme hook.
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: '#009ADA',
secondary: 'red',
},
}
Expected Behavior
Typescript should detect proper types.
How to reproduce
Add the above theme.
Your Environment
| software | version |
|---|---|
| iOS or Android | |
| @react-navigation/native | 5.8.10 |
| react-native | 0.63.4 |
| expo | 40.0.0 |
| node | 12+ |
| npm or yarn | yarn |
I tried to redeclare the theme in seprate global.d.ts file but that doesnt work either:
export declare type Theme = {
dark: boolean;
colors: {
primary: string;
background: string;
card: string;
text: string;
border: string;
notification: string;
testColor: string;
};
};
I have successfully extended the theme by using an interface like this one:
export interface CustomTheme extends Theme {
custom: {
colors: {
customColor: string
}
}
But now as I think about it, a type like this one might work just the way you want it to, by extending the existing colors property:
type CustomTheme = {
colors: {
customColor: string
}
} & Theme
Hope these help.
How do you use the costume theme then? When I am trying to use it I get a message, that Type 'Theme' is not assignable to type 'CustomTheme'. Property 'appColors' is missing in type 'Theme' but required in type '{ colors: { button: string; placeholder: string; }; appColors: ... }
I declare it like that in my theme file:
export type CustomTheme = {
colors: {
button: string;
placeholder: string;
};
appColors: {
primary: string;
background: {
backgroundColor: string;
};
card: {
backgroundColor: string;
};
button: {
backgroundColor: string;
borderColor: string;
};
card_2: {
backgroundColor: string;
};
textReversed: {
color: string;
};
text: {
color: string;
};
};
} & Theme;
and I want to use it like that in a screen:
const {appColors}: CustomTheme = useTheme();
I use it mostly via the component props, but where I use the useTheme hook I simply cast it to CustomTheme.
const {appColors} = useTheme() as CustomTheme
If you use it in many places, then you could create a wrapper for it.
const useCustomTheme = useTheme as () => CustomTheme
const {appColors} = useCustomTheme()
Please note that I have not tested the wrapper code in an app, simply that it passes type checking. Also I found out how react-native-paper suggests you extend the Theme interface with the global augmentation: https://callstack.github.io/react-native-paper/theming.html#typescript Maybe you should try that first.
I must admit I'm fairly new to ts. So your recommendation to start with the global augmentation didn't work out for me the easy way. My EsLint is complaining about the namespace ("ES2015 module syntax is preferred over custom TypeScript modules and namespaces") and I don't really know how to fix this, neither do I want to mess with the google TSLint recommendations ...
Casting in this case is perhaps the easier way. As long as I am not running into trouble, I will use this as a fast start. Thanks for the fast help 👍
I did this in a global *.d.ts file, and it seems to work alright.
import '@react-navigation/native';
// Override the theme in react native navigation to accept our custom theme props.
declare module '@react-navigation/native' {
export type ExtendedTheme = {
dark: boolean;
colors: {
primary: string;
secondary: string;
tertiary: string;
danger: string;
background: string;
card: string;
text: string;
subtext: string;
separator: string;
border: string;
highlight: string;
notification: string;
};
};
export function useTheme(): ExtendedTheme;
}
And then I declare the theme in a theme.ts file like so:
import { DefaultTheme, DarkTheme, ExtendedTheme } from '@react-navigation/native';
export const light: ExtendedTheme = {
dark: false,
colors: {
...DefaultTheme.colors,
primary: 'rgb(216, 11, 140)',
secondary: 'rgb(0, 174, 239)',
tertiary: 'rgb(34, 31, 114)',
danger: 'rgb(208, 2, 27)',
background: 'rgb(239, 238, 244)',
card: 'rgb(255, 255, 255)',
text: 'rgb(0, 0, 0)',
subtext: 'rgb(102, 102, 102)',
separator: 'rgb(194, 194, 195)',
highlight: 'rgb(199, 198, 203)',
},
};
Can you just provide a generic T please?
@klandell

does your editor can auto import this? i press and nothing happens..:(
@zmnv Hmm, I guess auto import in VSCode doesn't work for me either with the type override. I generally just copy and paste that sort of code so I never noticed.
@klandell answer works. Thanks!
We can go further by getting the type from the theme itself by usiing typeof. Considering klandell example, we can do something like this in the global.d.ts file:
//import the object theme you created
import { theme } from './theme'
declare module '@react-navigation/native' {
export type ExtendedTheme = typeof theme
export function useTheme(): ExtendedTheme;
}
This way you won't need to update your type every time you add/remove a property from your theme object :bulb:
P.S. The vscode auto-import feature doesn't work, indeed, but the autocomplete works.
Great
Hey guys, can i override the Theme type from react-navigation? Because i use DefaultThe from styled-components, that i override to build my own theme, and i want to override the Theme from react navigation with DefaultTheme from styled-components.
I override the DefaultTheme from react-native-paper with:
declare global {
namespace ReactNativePaper {
interface ThemeColors extends DefaultThemeColors {}
interface Theme extends StyledDefaultTheme {}
}
}
But i don't know how do the same with react-navigation. Any ideas?
Thanks for all your help. In my case I'm doing something like this:
types.ts file:
import { Theme as RNNTheme } from "@react-navigation/native";
interface Colors {
primary: string;
background: string;
card: string;
text: string;
border: string;
notification: string;
bottomTabActiveForeground: string;
bottomTabInactiveForeground: string;
}
interface Spacing {
s: number;
m: number;
l: number;
xl: number;
}
export interface Theme extends RNNTheme {
colors: Colors;
spacing: Spacing;
}
bottom-tab.tsx file:
import { useTheme } from "@react-navigation/native";
import * as React from "react";
function BottomTab() {
const { colors, spacing, ... } = useTheme() as Theme;
return <...>
}
export default BottomTab;
Hi everyone, I followed @klandell answer and created a *.d.ts file with the following:
import '@react-navigation/native';
declare module '@react-navigation/native' {
export type ExtendedTheme = {
dark: boolean;
colors: {
background: string;
card: string;
text: string;
textGrey: string;
};
};
export function useTheme(): ExtendedTheme;
}
But I still have an error when setting theme props of NavigationContainer:
Type 'ExtendedTheme' is not assignable to type 'Theme'
const Navigation = () => {
const scheme = useColorScheme();
return (
<NavigationContainer theme={scheme === 'dark' ? darkTheme : lightTheme}>
<StackNavigator />
</NavigationContainer>
);
};
export default Navigation;
Any idea how I can modify type of theme props ? I am pretty new to TS but I was not able to find the answer anywhere
Any idea how I can modify type of theme props ? I am pretty new to TS but I was not able to find the answer anywhere
This is what I recommend:
import {DefaultTheme, Theme} from '@react-navigation/native'
// Define extended theme type that literally *extends* Theme
interface ExtendedTheme extends Theme {
// Reference the Theme type's colors field and make our field an intersection
// Learn more:
// https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types
// https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html
colors: Theme['colors'] & {
textGrey: string;
}
}
export const lightTheme: ExtendedTheme = {
dark: false,
colors: {
// Define your custom colors here
// If you want to import the default theme's colors, uncomment the following line:
// ...DefaultTheme.colors
}
}
export const darkTheme: ExtendedTheme = {
dark: true,
colors: {
// Define your custom colors here
// If you want to import the default theme's colors, import DarkTheme within the import at the top and uncomment the following line:
// ...DarkTheme.colors
}
}
declare module '@react-navigation/native' {
export function useTheme(): ExtendedTheme
}
@evanwalsh thanks it works like a charm !
in customTheme.tsx: import {Theme} from '@react-navigation/native'; export interface CustomTheme extends Theme { primary: string; secondary: string; info: string; ...... }
In customTheme.d.tsx: import {CustomTheme} from './customTheme'; //path of the "customTheme.tsx" file declare module '@react-navigation/native' { export function useTheme(): CustomTheme; }
in LightTheme.tsx: import {CustomTheme} from '../interfaces/theme/customTheme'; //path of the "customTheme.tsx" file import {DefaultTheme} from '@react-navigation/native';
const lightTheme: CustomTheme = { ...DefaultTheme, primary: colors.primary, secondary: colors.secondary, info: colors.info, ..... }
import {NavigationContainer} from '@react-navigation/native';
< NavigationContainer theme={lightTheme}>
{......}
</ NavigationContainer>
It is Correct 100%
This worked for me, adding it to the global.d.ts file
export type Theme = {
dark: boolean;
colors: {
primary: string;
background: string;
card: string;
inputBg: string;
text: string;
border: string;
notification: string;
};
};
declare module "@react-navigation/native" {
export function useTheme(): Theme;
}
In React Navigation 7, you can override the theme with:
import { type Theme as NativeTheme } from '@react-navigation/native';
declare global {
namespace ReactNavigation {
interface Theme extends NativeTheme {
colors: NativeTheme['colors'] & {
// Your custom colors
warning: string;
};
}
}
}
Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.