Slate: Reimagine the placeholder api

Created on 10 Oct 2017  Â·  5Comments  Â·  Source: ianstormtaylor/slate

The current <Placeholder> component is confusing and seems to give people lots of issues. If anyone has a better design for it, I'd love to hear it!

idea ♥ help

All 5 comments

@ianstormtaylor Based on my own early experience with Placeholder, and what I saw in Slack questions early on, I think a lot of the confusion was around the fact that the Placeholder requires node and parent as a prop.

This is because it tries to incorporate behavior around rendering only the given node is the first child. I feel like clarifying those props, or even removing the need for them by inferring would be very useful. This also led to confusion if Placeholder text should be a child or a sibling of the Placeholder component.

One could argue that the Placeholder component is trying to accommodate two different types of placeholders (first paragraph in an empty document vs placeholder for a caption). While I personally think both of them should be core supported behaviors since they are fairly common, a reasonable counter-argument could be that the special first-only logic should be a plugin.

Assuming we want to keep both those behaviors, we could explore if Placeholder could be specified as a part of the schema for a node, like how decorate is specified.

@oyeanuj great write up! Specifying it as part of the schema is really interesting actually. A few different ideas for us to consider... (If anyone has more solutions please add them!)


1. Current Solution

The <Placeholder> component is exported, and a default is added by core to the default block.

The only nice thing about this is that it means that by default, when you create an editor and you add a placeholder="..." prop you can see a placeholder right away, mimicking the native <input>/<textarea> behavior nicely.

Actually there's one more pro, which is that the Placeholder as currently written lives inside the block component, so it takes on it's styling—it's the same font size, color, etc. by default.

Pretty much everything else is not nice though... as soon as you define a custom block you lose the placeholder, the Placeholder's API is confusing because it's solving two use cases as @oyeanuj points out, it has weird positioning logic to accommodate floats, etc.

2. In the Schema

We could add a placeholder property to the schema, which would render a placeholder node with any object that it matches. The default behavior it seems would be that it would match document only when document.text == ''. (It could even be made to work for inlines!)

This seems nicer in all aspects, because it gives us the default behavior, but leaves the placeholder rendering other than the default completely up to userland. And honestly wouldn't even require a <Placeholder> component to be exported at all. And defining a single placeholder to render for a user's document is dead simple.

Cons... depending on how it's implemented, the only con I can think of is that since it might not render inside the block/inline/etc, it wouldn't naturally take on the styling of the component, so it the default wouldn't match font size, color, etc.


I actually had thought of other solutions the other day, but I can't remember them now that I'm thinking about this schema solution because it feels like it fits so well...

@ianstormtaylor Yes, the second solution is along the lines of what I was thinking as the cleaner solution as well!

Cons... depending on how it's implemented, the only con I can think of is that since it might not render inside the block/inline/etc, it wouldn't naturally take on the styling of the component, so it the default wouldn't match font size, color, etc.

What if the placeholder property expects a component to be returned that is inserted as the child of the node in question? In that case, all a developer has to do is the following:

caption: {
  render: CaptionComponent, 
  placeholder: (attributes, node, state, editor) => <MyStyledPlaceholder {...attributes}>Placeholder text goes here</MyStyledPlaceholder>
}

In this case, the styling etc, should match since it is a component they are controlling. Does that align with how you were thinking about it?

@oyeanuj yup, that definitely aligns! I think that would be the only nice way to get the styles to work. It doesn't seem like there'd be any problem with that, as long as people are aware that placeholders render at that level.

Thanks so much for the great solution! I'll mull some other ones over for a bit, but it doesn't seem like we're going to find a better one 😄

Glad I can contribute back!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ianstormtaylor picture ianstormtaylor  Â·  3Comments

AlexeiAndreev picture AlexeiAndreev  Â·  3Comments

ianstormtaylor picture ianstormtaylor  Â·  3Comments

chrpeter picture chrpeter  Â·  3Comments

adrianclay picture adrianclay  Â·  3Comments