1 | [![Maintenance Status][maintenance-image]](#maintenance-status)
|
2 |
|
3 |
|
4 | <h1 align="center">
|
5 | prism-react-renderer 🖌️
|
6 | <br>
|
7 | </h1>
|
8 | <p align="center" style="font-size: 1.2rem;">
|
9 | A lean <a href="https://github.com/PrismJS/prism">Prism</a> highlighter component for React<br>
|
10 | Comes with everything to render Prismjs highlighted code directly to React (Native) elements, global-pollution-free!
|
11 | </p>
|
12 |
|
13 | ## Why?
|
14 |
|
15 | Maybe you need to render some extra UI with your Prismjs-highlighted code,
|
16 | or maybe you'd like to manipulate what Prism renders completely,
|
17 | or maybe you're just using Prism with React and are searching for an easier,
|
18 | global-pollution-free way?
|
19 |
|
20 | Then you're right where you want to be!
|
21 |
|
22 | ## How?
|
23 |
|
24 | This library tokenises code using Prism and provides a small render-props-driven
|
25 | component to quickly render it out into React. This is why it even works with
|
26 | React Native! It's bundled with a modified version of Prism that won't pollute
|
27 | the global namespace and comes with
|
28 | [a couple of common language syntaxes](./src/vendor/prism/includeLangs.js).
|
29 |
|
30 | _(There's also an [escape-hatch](https://github.com/FormidableLabs/prism-react-renderer#prism) to use your own Prism setup, just in case)_
|
31 |
|
32 | It also comes with its own [VSCode-like theming format](#theming), which means by default
|
33 | you can easily drop in different themes, use the ones this library ships with, or
|
34 | create new ones programmatically on the fly.
|
35 |
|
36 | _(If you just want to use your Prism CSS-file themes, that's also no problem)_
|
37 |
|
38 | ## Table of Contents
|
39 |
|
40 |
|
41 |
|
42 |
|
43 | - [Installation](#installation)
|
44 | - [Usage](#usage)
|
45 | - [Basic Props](#basic-props)
|
46 | - [children](#children)
|
47 | - [language](#language)
|
48 | - [code](#code)
|
49 | - [Advanced Props](#advanced-props)
|
50 | - [theme](#theme)
|
51 | - [Prism](#prism)
|
52 | - [Children Function](#children-function)
|
53 | - [state](#state)
|
54 | - [prop getters](#prop-getters)
|
55 | - [`getLineProps`](#getlineprops)
|
56 | - [`getTokenProps`](#gettokenprops)
|
57 | - [Theming](#theming)
|
58 | - [FAQ](#faq)
|
59 | - [LICENSE](#license)
|
60 |
|
61 |
|
62 |
|
63 | ## Installation
|
64 |
|
65 | This module is distributed via npm which is bundled with node and
|
66 | should be installed as one of your project's `dependencies`:
|
67 |
|
68 | ```sh
|
69 | # npm
|
70 | npm install --save prism-react-renderer
|
71 | # yarn
|
72 | yarn add prism-react-renderer
|
73 | ```
|
74 |
|
75 | > This package also depends on `react`. Please make sure you
|
76 | > have those installed as well.
|
77 |
|
78 | ## Usage
|
79 |
|
80 | > [Try it out in the browser](https://codesandbox.io/s/prism-react-renderer-example-u6vhk)
|
81 |
|
82 | <img src="./.readme/basic.png" width="237" alt="Screenshot showing highlighted code block" />
|
83 |
|
84 | ```jsx
|
85 | import React from "react";
|
86 | import { render } from "react-dom";
|
87 | import Highlight, { defaultProps } from "prism-react-renderer";
|
88 |
|
89 | const exampleCode = `
|
90 | (function someDemo() {
|
91 | var test = "Hello World!";
|
92 | console.log(test);
|
93 | })();
|
94 |
|
95 | return () => <App />;
|
96 | `;
|
97 |
|
98 | render((
|
99 | <Highlight {...defaultProps} code={exampleCode} language="jsx">
|
100 | {({ className, style, tokens, getLineProps, getTokenProps }) => (
|
101 | <pre className={className} style={style}>
|
102 | {tokens.map((line, i) => (
|
103 | <div {...getLineProps({ line, key: i })}>
|
104 | {line.map((token, key) => (
|
105 | <span {...getTokenProps({ token, key })} />
|
106 | ))}
|
107 | </div>
|
108 | ))}
|
109 | </pre>
|
110 | )}
|
111 | </Highlight>,
|
112 | document.getElementById('root')
|
113 | );
|
114 | ```
|
115 |
|
116 | `<Highlight />` is the only component exposed by this package, as inspired by
|
117 | [downshift](https://github.com/paypal/downshift).
|
118 |
|
119 | It also exports a `defaultProps` object which for basic usage can simply be spread
|
120 | onto the `<Highlight />` component. It also provides some default theming.
|
121 |
|
122 | It doesn't render anything itself, it just calls the render function and renders that.
|
123 | ["Use a render prop!"](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce)!
|
124 | `<Highlight>{highlight => <pre>/* your JSX here! */</pre>}</Highlight>`
|
125 |
|
126 | ### Line Numbers
|
127 |
|
128 | Add line numbers to your highlighted code blocks using the `index` parameter in your `line.map` function:
|
129 |
|
130 | <img src="./.readme/line-numbers.png" width="453" alt="Screenshot showing line numbers in highlighted code block" />
|
131 |
|
132 | For example, if you were using `styled-components`, it could look like the following demo.
|
133 |
|
134 | > [Demo with `styled-components`](https://codesandbox.io/s/prism-react-renderer-example-u6vhk?file=/src/WithLineNumbers.js)
|
135 |
|
136 | ```js
|
137 | import React from "react";
|
138 | import styled from "styled-components";
|
139 | import Highlight, { defaultProps } from "prism-react-renderer";
|
140 | import theme from "prism-react-renderer/themes/nightOwl";
|
141 |
|
142 | const exampleCode = `
|
143 | import React, { useState } from "react";
|
144 |
|
145 | function Example() {
|
146 | const [count, setCount] = useState(0);
|
147 |
|
148 | return (
|
149 | <div>
|
150 | <p>You clicked {count} times</p>
|
151 | <button onClick={() => setCount(count + 1)}>
|
152 | Click me
|
153 | </button>
|
154 | </div>
|
155 | );
|
156 | }
|
157 | `.trim();
|
158 |
|
159 | const Pre = styled.pre`
|
160 | text-align: left;
|
161 | margin: 1em 0;
|
162 | padding: 0.5em;
|
163 | overflow: scroll;
|
164 | `;
|
165 |
|
166 | const Line = styled.div`
|
167 | display: table-row;
|
168 | `;
|
169 |
|
170 | const LineNo = styled.span`
|
171 | display: table-cell;
|
172 | text-align: right;
|
173 | padding-right: 1em;
|
174 | user-select: none;
|
175 | opacity: 0.5;
|
176 | `;
|
177 |
|
178 | const LineContent = styled.span`
|
179 | display: table-cell;
|
180 | `;
|
181 |
|
182 | const WithLineNumbers = () => (
|
183 | <Highlight {...defaultProps} theme={theme} code={exampleCode} language="jsx">
|
184 | {({ className, style, tokens, getLineProps, getTokenProps }) => (
|
185 | <Pre className={className} style={style}>
|
186 | {tokens.map((line, i) => (
|
187 | <Line key={i} {...getLineProps({ line, key: i })}>
|
188 | <LineNo>{i + 1}</LineNo>
|
189 | <LineContent>
|
190 | {line.map((token, key) => (
|
191 | <span key={key} {...getTokenProps({ token, key })} />
|
192 | ))}
|
193 | </LineContent>
|
194 | </Line>
|
195 | ))}
|
196 | </Pre>
|
197 | )}
|
198 | </Highlight>
|
199 | );
|
200 |
|
201 | export default WithLineNumbers;
|
202 | ```
|
203 |
|
204 | ## Basic Props
|
205 |
|
206 | This is the list of props that you should probably know about. There are some
|
207 | [advanced props](#advanced-props) below as well.
|
208 |
|
209 | Most of these [advanced props](#advanced-props) are included in the `defaultProps`.
|
210 |
|
211 | ### children
|
212 |
|
213 | > `function({})` | _required_
|
214 |
|
215 | This is called with an object. Read more about the properties of this object in
|
216 | the section "[Children Function](#children-function)".
|
217 |
|
218 | ### language
|
219 |
|
220 | > `string` | _required_
|
221 |
|
222 | This is the language that your code will be highlighted as. You can see a list
|
223 | of all languages that are supported out of the box [here](./src/vendor/prism/includeLangs.js). Not all languages are included and the list of languages that are currently is a little arbitrary. You can use the [escape-hatch](https://github.com/FormidableLabs/prism-react-renderer#prism) to use your own Prism setup, just in case, or [add more languages to the bundled Prism.](https://github.com/FormidableLabs/prism-react-renderer#faq)
|
224 |
|
225 | ### code
|
226 |
|
227 | > `string` | _required_
|
228 |
|
229 | This is the code that will be highlighted.
|
230 |
|
231 | ## Advanced Props
|
232 |
|
233 | ### theme
|
234 |
|
235 | > `PrismTheme` | _required; default is provided in `defaultProps` export_
|
236 |
|
237 | If a theme is passed, it is used to generate style props which can be retrieved
|
238 | via the prop-getters which are described in "[Children Function](#children-function)".
|
239 |
|
240 | A default theme is provided by the `defaultProps` object.
|
241 |
|
242 | Read more about how to theme `react-prism-renderer` in
|
243 | the section "[Theming](#theming)".
|
244 |
|
245 | ### Prism
|
246 |
|
247 | > `PrismLib` | _required; default is provided in `defaultProps` export_
|
248 |
|
249 | This is the [Prismjs](https://github.com/PrismJS/prism) library itself.
|
250 | A vendored version of Prism is provided (and also exported) as part of this library.
|
251 | This vendored version doesn't pollute the global namespace, is slimmed down,
|
252 | and doesn't conflict with any installation of `prismjs` you might have.
|
253 |
|
254 | If you're only using `Prism.highlight` you can choose to use `prism-react-renderer`'s
|
255 | exported, vendored version of Prism instead.
|
256 |
|
257 | But if you choose to use your own Prism setup, simply pass Prism as a prop:
|
258 |
|
259 | ```jsx
|
260 | // Whichever way you're retrieving Prism here:
|
261 | import Prism from 'prismjs/components/prism-core';
|
262 |
|
263 | <Highlight Prism={Prism} {/* ... */} />
|
264 | ```
|
265 |
|
266 | ## Children Function
|
267 |
|
268 | This is where you render whatever you want to based on the output of `<Highlight />`.
|
269 | You use it like so:
|
270 |
|
271 | ```js
|
272 | const ui = (
|
273 | <Highlight>
|
274 | {highlight => (
|
275 | // use utilities and prop getters here, like highlight.className, highlight.getTokenProps, etc.
|
276 | <pre>{/* more jsx here */}</pre>
|
277 | )}
|
278 | </Highlight>
|
279 | );
|
280 | ```
|
281 |
|
282 | The properties of this `highlight` object can be split into two categories as indicated below:
|
283 |
|
284 | ### state
|
285 |
|
286 | These properties are the flat output of `<Highlight />`. They're generally "state" and are what
|
287 | you'd usually expect from a render-props-based API.
|
288 |
|
289 | | property | type | description |
|
290 | | ----------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
291 | | `tokens` | `Token[][]` | This is a doubly nested array of tokens. The outer array is for separate lines, the inner for tokens, so the actual content. |
|
292 | | `className` | `string` | This is the class you should apply to your wrapping element, typically a `<pre>` |
|
293 |
|
294 | A "Token" is an object that represents a piece of content for Prism. It has a `types` property, which is an array
|
295 | of types that indicate the purpose and styling of a piece of text, and a `content` property, which is the actual
|
296 | text.
|
297 |
|
298 | You'd typically iterate over `tokens`, rendering each line, and iterate over its items, rendering out each token, which is a piece of
|
299 | this line.
|
300 |
|
301 | ### prop getters
|
302 |
|
303 | > See
|
304 | > [Kent C. Dodds' blog post about prop getters](https://blog.kentcdodds.com/how-to-give-rendering-control-to-users-with-prop-getters-549eaef76acf)
|
305 |
|
306 | These functions are used to apply props to the elements that you render. This
|
307 | gives you maximum flexibility to render what, when, and wherever you like.
|
308 |
|
309 | You'd typically call these functions with some dictated input and add on all other
|
310 | props that it should pass through. It'll correctly override and modify the props
|
311 | that it returns to you, so passing props to it instead of adding them directly is
|
312 | advisable.
|
313 |
|
314 | | property | type | description |
|
315 | | --------------- | -------------- | ----------------------------------------------------------------------------------------------------- |
|
316 | | `getLineProps` | `function({})` | returns the props you should apply to any list of tokens, i.e. the element that contains your tokens. |
|
317 | | `getTokenProps` | `function({})` | returns the props you should apply to the elements displaying tokens that you render. |
|
318 |
|
319 | #### `getLineProps`
|
320 |
|
321 | You need to add a `line` property (type: `Token[]`) to the object you're passing to
|
322 | `getLineProps`; It's also advisable to add a `key`.
|
323 |
|
324 | This getter will return you props to spread onto your line elements (typically `<div>s`).
|
325 |
|
326 | It will typically return a `className` (if you pass one it'll be appended), `children`,
|
327 | `style` (if you pass one it'll be merged). It also passes on all other props you pass
|
328 | to the input.
|
329 |
|
330 | The `className` will always contain `.token-line`.
|
331 |
|
332 | #### `getTokenProps`
|
333 |
|
334 | You need to add a `token` property (type: `Token`) to the object you're passing to
|
335 | `getTokenProps`; It's also advisable to add a `key`.
|
336 |
|
337 | This getter will return you props to spread onto your token elements (typically `<span>s`).
|
338 |
|
339 | It will typically return a `className` (if you pass one it'll be appended), `children`,
|
340 | `style` (if you pass one it'll be merged). It also passes on all other props you pass
|
341 | to the input.
|
342 |
|
343 | The `className` will always contain `.token`. This also provides full compatibility with
|
344 | your old Prism CSS-file themes.
|
345 |
|
346 | ## Theming
|
347 |
|
348 | The `defaultProps` you'd typically apply in a basic use-case, contain a default theme.
|
349 | This theme is [duotoneDark](./src/themes/duotoneDark.js).
|
350 |
|
351 | While all `className`s are provided with `<Highlight />`, so that you could use your good
|
352 | old Prism CSS-file themes, you can also choose to use `react-prism-renderer`'s themes like so:
|
353 |
|
354 | ```jsx
|
355 | import dracula from 'prism-react-renderer/themes/dracula';
|
356 |
|
357 | <Highlight theme={dracula} {/* ... */} />
|
358 | ```
|
359 |
|
360 | These themes are JSON-based and are heavily inspired by VSCode's theme format.
|
361 |
|
362 | Their syntax, expressed in Flow looks like the following:
|
363 |
|
364 | ```js
|
365 | {
|
366 | plain: StyleObj,
|
367 | styles: Array<{
|
368 | types: string[],
|
369 | languages?: string[],
|
370 | style: StyleObj
|
371 | }>
|
372 | }
|
373 | ```
|
374 |
|
375 | The `plain` property provides a base style-object. This style object is directly used
|
376 | in the `style` props that you'll receive from the prop getters, if a `theme` prop has
|
377 | been passed to `<Highlight />`.
|
378 |
|
379 | The `styles` property contains an array of definitions. Each definition contains a `style`
|
380 | property, that is also just a style object. These styles are limited by the `types`
|
381 | and `languages` properties.
|
382 |
|
383 | The `types` properties is an array of token types that Prism outputs. The `languages`
|
384 | property limits styles to highlighted languages.
|
385 |
|
386 | When converting a Prism CSS theme it's mostly just necessary to use classes as
|
387 | `types` and convert the declarations to object-style-syntax and put them on `style`.
|
388 |
|
389 | ## FAQ
|
390 |
|
391 | <details>
|
392 |
|
393 | <summary>How do I add more language highlighting support?</summary>
|
394 |
|
395 | By default `prism-react-renderer` only includes an [arbitrary subset of the languages](https://github.com/FormidableLabs/prism-react-renderer/blob/master/src/vendor/prism/includeLangs.js) that Prism supports. You can add support for more by including their definitions from the main `prismjs` package:
|
396 |
|
397 | ```js
|
398 | import Prism from "prism-react-renderer/prism";
|
399 |
|
400 | (typeof global !== "undefined" ? global : window).Prism = Prism;
|
401 |
|
402 | require("prismjs/components/prism-kotlin");
|
403 | require("prismjs/components/prism-csharp");
|
404 | ```
|
405 | </details>
|
406 |
|
407 | <details>
|
408 |
|
409 | <summary>How do I use my old Prism css themes?</summary>
|
410 |
|
411 | `prism-react-renderer` still returns you all proper `className`s via the prop getters,
|
412 | when you use it. By default however it uses its new theming system, which output a
|
413 | couple of `style` props as well.
|
414 |
|
415 | If you don't pass `theme` to the `<Highlight />` component it will default to not
|
416 | outputting any `style` props, while still returning you the `className` props, like
|
417 | so:
|
418 |
|
419 | ```js
|
420 | <Highlight
|
421 | {...defaultProps}
|
422 | code={exampleCode}
|
423 | language="jsx"
|
424 | theme={undefined}
|
425 | >
|
426 | {highlight => null /* ... */}
|
427 | </Highlight>
|
428 | ```
|
429 |
|
430 | </details>
|
431 |
|
432 | <details>
|
433 |
|
434 | <summary>How do I prevent a theme and the vendored Prism to be bundled?</summary>
|
435 |
|
436 | Since the default theme and the vendored Prism library in `prism-react-renderer`
|
437 | come from `defaultProps`, if you wish to pass your own Prism library in, and not
|
438 | use the built-in theming, you simply need to leave it out to allow your bundler
|
439 | to tree-shake those:
|
440 |
|
441 | ```js
|
442 | import Highlight from "prism-react-renderer";
|
443 | import Prism from "prismjs"; // Different source
|
444 |
|
445 | <Highlight Prism={Prism} code={exampleCode} language="jsx">
|
446 | {highlight => null /* ... */}
|
447 | </Highlight>;
|
448 | ```
|
449 |
|
450 | You can also import the vendored Prism library on its own:
|
451 |
|
452 | ```js
|
453 | import { Prism } from "prism-react-renderer";
|
454 | // or
|
455 | import Prism from "prism-react-renderer/prism";
|
456 | ```
|
457 |
|
458 | </details>
|
459 |
|
460 | ## LICENSE
|
461 |
|
462 | MIT
|
463 |
|
464 | ## Maintenance Status
|
465 |
|
466 | **Active:** Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.
|
467 |
|
468 | [maintenance-image]: https://img.shields.io/badge/maintenance-active-green.svg
|
469 |
|