In this lesson, we'll learn about how you can style react components using the style prop and className prop. We will go over how these props are differant than regular html style and class properties. Style, in JSX, takes an object, camel casing the property keys like borderRadius
instead of border-radius
.
[00:00] Let's start out by adding a div with box in it. Then we'll say style is an object with padding left at 20 pixels. This is one of the handful of differences you'll find with JSX and HTML. Rather than a string which you would use in normal HTML, we're using an object here. The property keys are camel cased rather than combop cased as you would have in CSS. The property values are strings.
[00:26] This is nice because objects are much easier to compose together than strings of CSS. We'll talk more about that in a second. Also, because the padding left value is pixels, we can change it to a number of 20 instead of a string. React will treat that as a pixel value.
[00:43] Next, we'll say classname equals box to apply the CSS we have on this page. Let's add box--small to get a small, square box. You'll notice here that we're using the classname prop. This is another one of the differences with JSX and HTML.
[00:58] In HTML, we'd use the attribute class whereas here we're using classname. If we were to extract these props into an object like this then spread them out on the div, we'd get the same result.
[01:10] Now, let's extract the classname value into a variable called classname and use object shorthand to add it to the props. Cool. It's all still working. If the classname prop were class instead, we wouldn't be able to use the shorthand here because you can't have variables called class. That's a syntax error.
[01:28] In addition, if we tried to destructure class out of the props object here, that wouldn't work either. The poor ergonomics of using a variable called class in JavaScript is one of the reasons React chose classname over class for this particular prop.
[01:42] Let's go ahead and turn this into a functioning component called box. We'll accept all the props and spread them across the div we rendered, so you can pass other props to the box component. Let's render that here with small box. Now, let's add a style prop to the box with background color light blue.
[02:01] Now, we've lost our padding on the box. This is because object spread does a shallow merge of the objects given, so the style prop given to the box component is overriding its own style prop.
[02:13] Let's destructure the props, pull out the style prop, and call the rest of the props rest. Then we'll spread the rest props onto the div. Then we'll merge the style prop with our own styles. Awesome, that works. These things are composing super well. This would be much more difficult to do if the style prop accepted a string of CSS. I'm glad that it's an object instead.
[02:36] Let's do the same for classname so we can have different sized boxes. We'll remove the box--small and instead pass that into where we're rendering the box. We'll say class name equals box--small. We're going to have the same problem we had before, so we'll need to merge these two props together ourselves.
[02:55] Let's destructure classname out and assign the classname on the div to a template literal instead. We'll say box and then interpolate the classname that we're given. Cool, that's working. There's one more consideration here. Let's remove the classname from where we're rendering the box and check out the DOM.
[03:15] You'll see that we're getting the classname of undefined. That's probably OK, but let's go ahead and clean it up. This is happening because the classname prop does not exist, so it's undefined. We'll add a default value for the classname to be an empty string.
[03:28] Now, that's gone and there's an empty space there. We could add a trim to this template literal like this to get rid of that space, but I don't really think that's necessary. All right. Awesome.
[03:39] Now, let's go ahead and render a couple other boxes, each with a different color and size. We'll do medium, we'll make that pink, and then we'll do large, and we'll make that orange. This is composing together really nicely.
[03:55] One concern I have with this is that the classname to get these different sizes has to be known by the users of the box component. It'd be much better if the users could tell the box what size it should be and then the box could apply the right classname itself. That way, all of the styling logic for the box resides in the box component.
[04:16] Instead of a classname prop, let's use a size prop where we can pass a string for the size like small. Let's destructure out the size prop and calculate classname based on that value. We'll say const size classname equals size ternary box--size or empty string. Then we'll interpolate that into our classname prop.
[04:41] Now, this box works exactly as it had before only now the relevant styling information is entirely contained in the box component. We can still pass overrides for the in-line styles and classnames. This makes it really nicely composable. Let's go ahead and do the same thing for the medium and large boxes just for good measure.
[05:00] In review, to style a React component, you can use the classname prop to assign classnames used in regular CSS styles, and you can use the style prop which accepts an object of CSS. One more important thing to note about the style prop is that the values are not vendor prefixed for you, so you'll have to do that yourself.
[05:21] There are a couple other problems within in-line styles as well. There are a couple libraries to overcome some of these problems. Popular libraries include styled components, emotion, and glamorous. I highly recommend you give these a look if you're planning on building a serious React application.
You forgot to close </Box>
:
function Box(style, ...rest) {
return <div className="box box--medium" style={{...style}} {...rest} />
}
const element = <Box style={{backgroundColor: 'lightblue'}}>small box</Box>
Great course @kent ! I believe we can strip the className after using the size prop
function Box({
style,
size,
...rest
}) {
const sizeClassName = size ? `box--${size}` : ''
return (
<div
className={`box ${sizeClassName}`}
style={{paddingLeft: 20, ...style}}
{...rest}
/>
)
}
Kent, Please update the below code in the transcript. You are using javascript notation inside the div tag.
function Box(props){
return (
<div
className: 'box box--small',
style: {paddingLeft: 20},
{...props}
/>
)
}
Great course @kent ! I believe we can strip the className after using the size prop
function Box({
style,
size,
...rest
}) {
const sizeClassName = size ? `box--${size}` : ''
return (
<div
className={`box ${sizeClassName}`}
style={{paddingLeft: 20, ...style}}
{...rest}
/>
)
}
Yea, this works too :)
Thx again for this great course,
Be carefull in this transcript :
you can replace :
className={{box ${className}
}}
by:
{box ${className}
}
Small spelling error in first paragraph: "We will go over how these props are "differant" (should be different) than regular html style..."
"Let's destructure the props, pull out the style prop, and call the rest of the props rest. Then we'll spread the ...rest props onto the <div>. "
What is the word "rest" in this scenario? Is it some keyword that React recognizes? Or is it some arbitrary name you gave the props?
Thanks for the tip Alex!
So "rest" refers to "rest" syntax. It's a JavaScript feature that has unique application in JSX as I demonstrate in the video. The variable name does not have to be called "rest"
Is there a guideline for when to use "function" and when to use "const"?
const props = {
className: "box--small",
style: { marginLeft: 100 },
}
return (
<div>
<div { ...props } >
Box
</div>
</div>
);
Why do we end the final line with a comma, given that there's not another entry after that?
const props = {
className: "box--small",
style: { marginLeft: 100 },
}
at 1:17, what is "object shorthand"?
Keith, you use functions for things that accept parameters. For example, at the beginning of the lesson, Box is a function that takes a parameter named props. Later on we add more parameters, such as size, className, style, and ...rest, which is a placeholder for anything else passed in.
Meanwhile, props is a const because it's an object with fixed values.
The reason why the last element in props has a comma even though there are no more elements, is a kind of cheat that allows for a defensive programming style. It's just a convention that was adopted by a few programming languages, not just JS, that makes it easier to add a new element to an object, list, etc, without inviting syntax errors. Here's a short article on the rationale: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas
the following code:
function Box(style, ...rest) { return ( <div className = 'box box--medium' style= {{ ...style}} {...rest} /> ) }
const element = ( <div> <Box style={{backgroundColor: 'lightblue'}}>small box</Box> </div> )
gives me the following error:
babel.js:61666 You are using the in-browser Babel transformer. Be sure to precompile your scripts for production - https://babeljs.io/docs/setup/ runScripts @ babel.js:61666 transformScriptTags @ babel.js:336 (anonymous) @ babel.js:327 react-dom.development.js:1101 Warning: Invalid attribute name:
0
printWarning @ react-dom.development.js:1101 warning @ react-dom.development.js:1125 isAttributeNameSafe @ react-dom.development.js:3953 setValueForAttribute @ react-dom.development.js:4109 setValueForProperty @ react-dom.development.js:4093 setInitialDOMProperties @ react-dom.development.js:7003 setInitialProperties @ react-dom.development.js:7166 finalizeInitialChildren @ react-dom.development.js:18188 completeWork @ react-dom.development.js:12031 completeUnitOfWork @ react-dom.development.js:13591 performUnitOfWork @ react-dom.development.js:13690 workLoop @ react-dom.development.js:13792 callCallback @ react-dom.development.js:1555 invokeGuardedCallbackDev @ react-dom.development.js:1594 invokeGuardedCallback @ react-dom.development.js:1451 performWork @ react-dom.development.js:13910 scheduleUpdateImpl @ react-dom.development.js:14295 scheduleUpdate @ react-dom.development.js:14234 scheduleTopLevelUpdate @ react-dom.development.js:14507 updateContainer @ react-dom.development.js:14537 (anonymous) @ react-dom.development.js:18376 unbatchedUpdates @ react-dom.development.js:14366 renderSubtreeIntoContainer @ react-dom.development.js:18375 render @ react-dom.development.js:18400 (anonymous) @ Inline Babel script:62 run @ babel.js:61531 check @ babel.js:61597 loadScripts @ babel.js:61638 runScripts @ babel.js:61668 transformScriptTags @ babel.js:336 (anonymous) @ babel.js:327 babel.js:326 [Violation] 'DOMContentLoaded' handler took 175ms