Hey there, hope you’re well! If you’re interested in learning to develop seriously with WordPress and React you found the right article. The WordPress packages, and many other open source packages use Typescript in development to make coding more consistent and to prevent errors. But if you’re just starting out, it can be difficult to know how to work with a project like WordPress and Typescript together with React. The aim of this simple project is to help you get comfortable with the main concepts of Typescript in developing with WordPress.
Estimated reading time: 14 minutes
What are we building?
We’re pulling WordPress into a React App to make it easy to understand and easy to get started. For this project you don’t need to create a WordPress plugin or theme. In fact there is no Active WordPress installation required! We’re doing everything in the code editor and terminal.
The App we’re going to create simply generates a notice when a button is clicked. This app is fashioned after the WordPress style guides so the hierarchy of the buttons is easy to understand. Below is our App in its default state:

This is the App after all of the buttons have been clicked:

Now that we know what we’ll be doing, let’s go ahead and get started.
Want to learn how to hide a block from a template or template part in a block theme? Check out our step by step developer guide here.
Create a Typescript-React project
First create the React App with a Typescript template.
- Navigate to a folder on your computer, in VS Code
- Paste the below code in your VS Code terminal.
- In the code below, replace the “name-of-app” string to whatever you want to name your project as.
npx create-react-app name-of-app --template typescript
In your terminal, be sure to change directories into your project folder:
cd name-of-app
Downgrade React to Install WordPress Components
Next, before you can install any WordPress packages, you’ll need to downgrade your version of React from 18 to @17.0.2.
To do this first find these three packages and delete them from your package.json
"react": version number
"react-dom": version number
"@testing-library/react": version number
Next up remove your package-lock.json and node-modules by pasting each of these commands in your terminal and hit enter, (one after the other).
rm package-lock.json
rm -r node_modules
Install the version of react that works with WordPress Components:
npm install react@17.0.2
After that, install the following wordpress packages:
npm install @wordpress/element @wordpress/components
Now you have all of the package dependencies working together for the project.
Update your index.js file to use React 17
Because React 18 changed the react-dom package and the way to render your app on the screen, we’ll need to roll back the index.js file to the React 17 method. When you first created your React App, React set up your index.js file like this:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Due to changes with the ReactDOM library in React 18, you’ll need to readjust your index.js file like this:
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
ReactDOM.render(<App />, document.querySelector('#root'))
Be sure to remove the reportWebVitals() function.
Scaffold the WordPress in a React-Typescript App
Now that you’re ready to render your component on the screen, you’ll need to clean up some of the files and add in your components folder.
- Remove the App.test.tsx file as you won’t need to do Unit Tests for this project, (unless you want to that’s totally cool just keep in mind that it can prevent your app from loading if you don’t adjust it appropriately).
- Remove the logo file that comes with all brand new React apps.
- In your App.js file remove everything for now.
- In your src/ directory create a folder and name it components/
- In your App.js file add in the following to get started:
import React from 'react'
const App = () => {
return <div className="App">App</div>
}
export default App
Now you have a solid foundation to build from. Go ahead and run the following command in your terminal, a div that says “App” should appear in your browser.
npm start
Like working with React and Typescript? Check out our post here on how to set up React Testing Library in a Vite React project.
Create your own Button Component
Now we can begin working with the WordPress Components package. To use WordPress in a React-Typescript Project, let’s start by creating a file in the components directory and title it WPButton.tsx. The .tsx file extension tells Typescript that you’ll be creating JSX in this file. Files that end in .ts tell Typescript that there is just Typescript code in the file and no JSX.
At the top of your new WPButton.tsx file import React:
import React from 'react'
Then under this you’ll import WordPress Components, add this line of code after the one above:
import { Button } from '@wordpress/components'
Underneath this we’ll scaffold out our button component:
const WPButton = () => {
return <Button></Button>
}
export default WPButton
We need to tell Typescript what this button is and how it’s going to be used. Add the following to your button component:
const WPButton:React.FC = () => {
return <Button></Button>
}
You just told Typescript that this component is a React Functional Component. Now Typescript is going to yell at you when you don’t specify types for your props ?.
Like making buttons with React or working with Open Source libraries? Check out this fun walk rough on creating Toggle Buttons with Material UI!
Create a Props interface
An interface allows you to set types for all of your props that your component may use. This is really helpful in the case of other people coming in to use this component. They might think one of your props is a boolean, but it really takes a string. Using Typescript and a Props Interface allows others (and future you) to be aware of the allowable type of any given prop.
Above your component add in an interface like this:
interface WPButtonProps {
}
Because I read the WordPress Button component documentation, I know the props I’m going to want to use from the package, as well as the props I’m going to add in myself. The props I need for my own use are something with a boolean value that will set my button to disabled when true, I may need to add a children prop so I can add HTML elements, or text inside my button, and I need an onClick function too. Here’s how we add these prop type definitions to our Interface:
clicked: boolean
onClick: () => void
children?: React.ReactNode | string
From the WordPress Button documentation I know I am going to use a couple of the available props they use. I may want to use the variant prop (which takes a string), and I may want to use the isDestructive prop, which is a boolean value.
variant?: string
isDestructive?: boolean
Notice that I have set a question mark before the colon for each prop that I may or may not use in the future! This means I don’t have to define these props on my component in the future which is good because I may not need to in certain situations.
Your WPButton interface should look like this:
interface WPButtonProps {
clicked: boolean
onClick: () => void
children?: React.ReactNode | string
variant?: string
isDestructive?: boolean
}
Now you need to tell your WPButton component to use these Prop Type Definitions by adding the interfce to the component like so:
const WPButton: React.FC<WPButtonProps> = ()
Next you need to define the props on the component:
const WPButton: React.FC<WPButtonProps> = ({
clicked,
onClick,
children,
variant,
isDestructive
}) => {
return (
<Button isDestructive={isDestructive} variant={variant} onClick={onClick} disabled={clicked}>
{children}
</Button>
)
}
Import your Button into App.js
Back in your App.js file, go ahead and import your brand new button. At the top of the file add this import:
import WPButton from './components/WPButton'
Within your App component, replace the App text with the WPButton component:
const App = () => {
return (
<div className='App'>
<WPButton>
Error Notice
</WPButton>
</div>
)
}
Scaffold your Buttons
Typescript should be yelling at you with red squiggly lines to add your Props to your button component. Go ahead and mouse over the squiggly line to read the error notice. It is telling you that the props from the interface that don’t have a question mark need to be set. Let’s add those in now along with the isDestructive and variant props. These will style our Error Notice button appropriately.
<WPButton
isDestructive={true}
variant='primary'
clicked={err}
onClick={onErrClick}
>
Error Notice
</WPButton>
Typescript should now be telling you that there is no type definition for err or onErrClick. We’ll get to handling state and events in a minute. For now, let’s insert the other three buttons in our app. Pay attention to the optional usage of the variant and isDesctructive props.
Your App component should now look like this:
const App = () => {
return (
<div className='App'>
<WPButton
isDestructive={true}
variant='primary'
clicked={err}
onClick={onErrClick}
>
Error Notice
</WPButton>
<WPButton
isDestructive={true}
variant='secondary'
clicked={warn}
onClick={onWarnClick}
>
Warning Notice
</WPButton>
<WPButton variant='primary' clicked={succ} onClick={onSuccClick}>
Success Notice
</WPButton>
<WPButton variant='secondary' clicked={info} onClick={onInfoClick}>
Info Notice
</WPButton>
</div>
)
}
Set Up State and Click Events
Now we can address these ugly squiggly lines in our buttons. First, we need to add the following to the top of our App.js file, in the React import statement:
import React, { useState } from 'react'
At the top of our App component, before the return statement let’s add in the following pieces of state:
const [err, setErr] = useState(false)
const [warn, setWarn] = useState(false)
const [succ, setSucc] = useState(false)
const [info, setInfo] = useState(false)
Next up let’s create our OnClick events. Under the state declarations, add the following functions:
const onErrClick = () => {
setErr(true)
}
const onWarnClick = () => {
setWarn(true)
}
const onSuccClick = () => {
setSucc(true)
}
const onInfoClick = () => {
setInfo(true)
}
Go ahead and run npm start if you haven’t already, and you should see all 4 buttons on the screen ? . The only problem is, they look kinda ugly, what gives? Well, we need to import the styles from the WordPress Components.
Import Styles from node modules
To ensure that the styling of our buttons is working, we need to navigate into the node-modules folder and copy over the build-styles/style.css file into our project.
- Navigate to app-name/node_modules/@wordpress/components/build-style/style.css and copy the file.
- In your src/components folder paste the style.css file.
- In your App.tsx file add this import to the top of the file:
import './components/style.css'
Here’s what your WordPress in a React-Typescript App component should look like so far:
import React, { useState } from 'react'
import WPButton from './components/WPButton'
import './components/style.css'
const App = () => {
const [err, setErr] = useState(false)
const [warn, setWarn] = useState(false)
const [succ, setSucc] = useState(false)
const [info, setInfo] = useState(false)
const onErrClick = () => {
setErr(true)
}
const onWarnClick = () => {
setWarn(true)
}
const onSuccClick = () => {
setSucc(true)
}
const onInfoClick = () => {
setInfo(true)
}
return (
<div className='App'>
<WPButton
isDestructive={true}
variant='primary'
clicked={err}
onClick={onErrClick}
>
Error Notice
</WPButton>
<WPButton
isDestructive={true}
variant='secondary'
clicked={warn}
onClick={onWarnClick}
>
Warning Notice
</WPButton>
<WPButton variant='primary' clicked={succ} onClick={onSuccClick}>
Success Notice
</WPButton>
<WPButton variant='secondary' clicked={info} onClick={onInfoClick}>
Info Notice
</WPButton>
</div>
)
}
export default App
Here’s how your Buttons should look so far:

How to get rid of the Typescript warning: could not find declaration for module ‘@wordpress/components’
If you’re getting the following warning “Could not find declaration for module ‘@wordpress/components’, here’s how to fix that:
In your /src directory create a file called types.d.ts and add this line of code in then hit save:
declare module '@wordpress/components'
That’s it!
Create your own Card Component
Now we can create a component from the WordPress Component package that doesn’t need quite so much configuration. To utilize WordPress and Typescript together with React here, we’re going to create a card thats usage won’t need to be altered. Because we don’t need to alter the usage of this component, we can hard code the props.
In your components/ directory, add a file and name it WPCard.tsx
Inside this file add this import statement to the top:
import { Card, CardBody } from '@wordpress/components'
Scaffold the Card component
Add the following to the file:
const WPCard: React.FC = () => {
return (
<>
<Card>
<CardBody></CardBody>
</Card>
</>
)
}
export default WPCard
“Hard coding” props
We will need an interface, but only for the children prop as we will be adding strings or JSX elements inside our card.
Create the interface:
interface WPCardProps {
children?: React.ReactNode | string
}
Finally update your WPCard component to use this Props Interface:
const WPCard: React.FC<WPCardProps> = ({children}) => {
return (
<>
<Card>
<CardBody size="large" isShady={true}>{children}</CardBody>
</Card>
</>
)
}
export default WPCard
Once again I am using props provided by WordPress from the Card Documentation here. The difference is, when I use this Card I won’t be changing its appearance, so I can hard code my props here and it will have a consistent look wherever else I decide to use it.
Want to learn more about using Github as a beginner? Check out this helpful step by step guide to get you up and running like a pro!
Importing your Card into App.js
Back in my App.js file, I’ll add this import statement at the top of my file with my other imports:
import WPCard from './components/WPCard'
And I’ll wrap up my Buttons inside this Card:
<div className='App'>
<WPCard>
<WPButton
isDestructive={true}
variant='primary'
clicked={err}
onClick={onErrClick}
>
Error Notice
</WPButton>
<WPButton
isDestructive={true}
variant='secondary'
clicked={warn}
onClick={onWarnClick}
>
Warning Notice
</WPButton>
<WPButton variant='primary' clicked={succ} onClick={onSuccClick}>
Success Notice
</WPButton>
<WPButton variant='secondary' clicked={info} onClick={onInfoClick}>
Info Notice
</WPButton>
</WPCard>
</div>
Import Flex and Notice Components into App.js
Now that I have my custom components in place, I can add the other WordPress components I want to use directly into my App.js file. I’ll add this import to the top of my App.js file:
import { Notice, Flex } from '@wordpress/components'
Next I’ll add an H1 tag and wrap my buttons in the Flex component. The top of my App.js component should now look like this. Be sure you add the ending </Flex> tag before the ending </WPCard> tag:
<div className='App'>
<WPCard>
<h1>Pick a Notice, any notice</h1>
<Flex>
<WPButton
The app should look something like this:

Conditionally Rendering the Notices
Because we have a boolean value tied to the state of each button, we can conditionally render the various Notice components we want to use based on the state of the button that will control it.
Before the WPCard component, add this line of code:
{err && (
<Notice onRemove={() => setErr(false)} status='error'>
There was an error oh nooo
</Notice>
)}
This Notice will load when the err piece of state is true. The Notice is using a function to set the err state back to false. This onRemove function and the status prop are both provided by the WordPress Notices Component. Learn more about this here.
Now let’s go ahead and add in our other Notices for each WPButton we’ve created. Ensure you have the 4 conditionally rendered components above the WPCard component inside your App component:
{err && (
<Notice onRemove={() => setErr(false)} status='error'>
There was an error oh nooo
</Notice>
)}
{warn && (
<Notice onRemove={() => setWarn(false)} status='warning'>
Warning: something is problematic, is it you?
</Notice>
)}
{succ && (
<Notice onRemove={() => setSucc(false)} status='success'>
OMG Way to go!!
</Notice>
)}
{info && (
<Notice onRemove={() => setInfo(false)} status='info'>
This is some info I hope it helps!
</Notice>
)}
Run npm start if you haven’t yet and test out clicking each button, and dismissing each notice. Pretty neat right?
Finishing touches of style
Because I don’t like the spacing of things as they are, and because I don’t want my app to move around on the page, I’m going to add some styling to fancy up this thing. First I’m going to rename the App className to ‘wrapper’:
<div className='wrapper'>
Underneath this, I’m going to wrap my Notices in a div and name it ‘notice-wrapper’ like so:
<div className='notice-wrapper'>
{err && (
<Notice onRemove={() => setErr(false)} status='error'>
There was an error oh nooo
</Notice>
)}
{warn && (
<Notice onRemove={() => setWarn(false)} status='warning'>
Warning: something is problematic, is it you?
</Notice>
)}
{succ && (
<Notice onRemove={() => setSucc(false)} status='success'>
OMG Way to go!!
</Notice>
)}
{info && (
<Notice onRemove={() => setInfo(false)} status='info'>
This is some info I hope it helps!
</Notice>
)}
</div>
Finally, I’m going to add this import to import the App.css file, which I definitely didn’t delete. (PS, if you deleted this file, go ahead and create a new one in your /src directory:
import './App.css'
In my App.css file I’m going to delete anything that’s currently there, and add the following styles:
.wrapper {
position: relative;
padding: 10%;
}
.notice-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.components-card {
margin-top: 20%;
}
@media screen and (max-width: 600px) {
.wrapper .components-flex {
flex-wrap: wrap;
gap: 7px;
justify-content: center;
}
.wrapper .components-button {
display: block;
width: 120px;
text-align: center;
}
}
@media screen and (max-width: 900px) {
.components-card {
margin-top: 50%;
}
}
These styles clean up the app and make it more mobile friendly.
Conclusion
Hopefully now you’ve gotten familiar with using WordPress and Typescript together with React to improve the code scalability of your App. Thanks so much for reading this far!
Photo by Fikret tozak on Unsplash