This is the repo for swyx's blog - Blog content is created in github issues, then posted on swyx.io as blog pages! Comment/watch to follow along my blog within GitHub
If you are using TypeScript with Preact aliased as React, you can add an ambient declaration to use class instead of className:
// anyname.d.ts - place this anywhere in your project
declare namespace React {
interface HTMLAttributes<T> {
// Preact supports using "class" instead of "classname" - need to teach typescript
class?: string;
}
}
My Context
Most React users aren't using every feature of React. For most usecases, using Preact is exactly equivalent. As point of proof: This blogpost you are reading is written in a custom CMS in Preact, by me, a React dev who has never used Preact and only skimmed the Differences page on their docs.
However I'm not here to write about anything as important as all that 😂. I'm here writing about a much smaller benefit of using Preact - you can use class instead of className!
Why class over className
This is mainly a pain point because I use Tailwind UI, which involves a lot of copying and pasting mountains of code that looks like this:
Now because I'm using Next.js and TypeScript with Preact, I use Preact with a React alias - basically lying to TypeScript that we are using React so we benefit from it's mature tooling across VS Code and Next.js.
However React doesn't use class for classes, it uses className! (At least until React Fire lands.) So I have two choices:
either go through and rename every class to className - like a heathen - every time I use Tailwind
or try to use class as Preact lets me do
The problem goes back to what I stated above: we lied to TypeScript that we're using React, so it's not going to let us use class:
This:
<div class="bg-black">Does this work?</div>
Leads to:
(JSX attribute) class: string
Type '{ children: string; href: string; class: string; }' is not assignable to type 'DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>'.
Property 'class' does not exist on type 'DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>'.ts(2322)
Peek Problem
No quick fixes available
Oh no! No quick fixes available??
Lies.
The Quick Fix
Here's the fix. Add a TypeScript ambient declaration - basically a anyname.d.ts file anywhere in your project, assuming default tsconfig settings and add this:
// anyname.d.ts - place this anywhere in your project
declare namespace React {
interface HTMLAttributes<T> {
// Preact supports using "class" instead of "classname" - need to teach typescript
class?: string;
}
}
And now I can write class in my React code!
If that's all you came to this blogpost for, then we're done. I'm just going to discuss how I made my way to this solution as an intermediate TypeScript user, since this is a learning opportunity for broader TypeScript use.
⚠️ If you're also using Tailwind with Next.js, there is one more issue to resolve - disabling the styled-jsx plugin. More details here: https://github.com/zeit/next.js/issues/11675
Appendix: Declaration Merging to Patch Definitions
I ultimately resorted to looking up the React typings in DefinitelyTyped itself, and realizing that React was exported as a namespace. Since Namespaces are a more arcane feature of TypeScript, I've never really used them in application code.
But it was enough to go on - I swapped declare module 'react' with declare namespace React and prayed that declaration merging worked.
source: devto devToUrl: "https://dev.to/swyx/how-to-use-class-instead-of-classname-with-preact-and-typescript-2bjh" devToReactions: 7 devToReadingTime: 5 devToPublishedAt: "2020-04-05T06:08:05.820Z" devToViewsCount: 1168 category: tutorial tags: [Tech, Preact, React, TypeScript]
Bottom Line Up Front
If you are using TypeScript with Preact aliased as React, you can add an ambient declaration to use
class
instead ofclassName
:My Context
Most React users aren't using every feature of React. For most usecases, using Preact is exactly equivalent. As point of proof: This blogpost you are reading is written in a custom CMS in Preact, by me, a React dev who has never used Preact and only skimmed the Differences page on their docs.
This is a good idea because of the limited downside - you can swap back to React in a single command if you end up needing its power - and the instant upside - A Next.js + Preact page bundle will now come in as low as 20kb of JS whereas a trip to the Create Next App docs will download at least 100kb of JS off the bat (note: these aren't apples to apples comparisons). If you'd like to give it a try, I've been building a small Preact + Next.js + TypeScript + TailwindCSS starter repo.
However I'm not here to write about anything as important as all that 😂. I'm here writing about a much smaller benefit of using Preact - you can use
class
instead ofclassName
!Why
class
overclassName
This is mainly a pain point because I use Tailwind UI, which involves a lot of copying and pasting mountains of code that looks like this:
The Problem
Now because I'm using Next.js and TypeScript with Preact, I use Preact with a React alias - basically lying to TypeScript that we are using React so we benefit from it's mature tooling across VS Code and Next.js.
However React doesn't use
class
for classes, it usesclassName
! (At least until React Fire lands.) So I have two choices:class
toclassName
- like a heathen - every time I use Tailwindclass
as Preact lets me doThe problem goes back to what I stated above: we lied to TypeScript that we're using React, so it's not going to let us use
class
:This:
Leads to:
Oh no! No quick fixes available??
Lies.
The Quick Fix
Here's the fix. Add a TypeScript ambient declaration - basically a
anyname.d.ts
file anywhere in your project, assuming defaulttsconfig
settings and add this:And now I can write
class
in my React code!If that's all you came to this blogpost for, then we're done. I'm just going to discuss how I made my way to this solution as an intermediate TypeScript user, since this is a learning opportunity for broader TypeScript use.
Appendix: Declaration Merging to Patch Definitions
One thing I solidified when I read Boris Cherny's TypeScript book is the power of Declaration Merging to fix issues with official typings. I even included this as part of the Troubleshooting Handbook in the React TypeScript Cheatsheet.
But I'd only patched libraries before, never patched something in the core behavior of JSX itself.
When I googled how to do this, I found this unhelpful Stackoverflow answer and eventually this Stackoverflow answer:
But when I tried it, it didn't work.
I ultimately resorted to looking up the React typings in DefinitelyTyped itself, and realizing that React was exported as a
namespace
. Since Namespaces are a more arcane feature of TypeScript, I've never really used them in application code.But it was enough to go on - I swapped
declare module 'react'
withdeclare namespace React
and prayed that declaration merging worked.It did. And now you know too.