You're browsing the documentation for an old version of MoonShine. Consider upgrading your project to MoonShine 2.x.

MoonShine provides opportunities to extend the basic functionality and write your own packages that improve the features. In this section, we will provide a list of such packages, an example with creating your own field and action.

If you can't figure out how your MoonShine package should look like, we've prepared a ready-made template for you. Package template

The following entities could be extended:

  • Fields
  • Filters
  • Decorations
  • Actions
  • Metrics
  • InputExtension
  • FormComponent
  • Resource

# Custom field

Here is a small example of creating your own field! It is a visual editor based on the CKEditor js plugin

First, let's create a class that extends the MoonShine fields

namespace App\MoonShine\Fields;
use MoonShine\Fields\Field;
final class CKEditor extends Field
protected static string $view = 'fields.ckeditor';
protected array $assets = [

Then, create a view with implementation

'id' => 'ckeditor_' . $item->getKey() . '_' . $element->id(),
'name' => $element->name()
>{!! $element->formViewValue($item) ?? '' !!}</x-moonshine::form.textarea>
CKEDITOR.ClassicEditor.create(document.getElementById("ckeditor_{{ $item->getKey() }}_{{ $element->id() }}"), {
toolbar: {
items: [
'heading', '|',
'bold', 'italic', 'strikethrough', 'underline', 'code', 'subscript', 'superscript', 'removeFormat', '|',
'bulletedList', 'numberedList', 'todoList',
'outdent', 'indent', '|',
'undo', 'redo',
'exportPDF','exportWord', '|',
'findAndReplace', '|',
'fontSize', 'fontColor', 'fontBackgroundColor',
'alignment', '|',
'link', 'insertImage', 'blockQuote', 'insertTable', 'mediaEmbed', 'codeBlock', 'htmlEmbed', '|',
'specialCharacters', 'horizontalLine', 'sourceEditing'
shouldNotGroupWhenFull: true
// Changing the language of the interface requires loading the language file using the <script> tag.
// language: 'es',
list: {
properties: {
styles: true,
startIndex: true,
reversed: true
heading: {
options: [
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
{ model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
{ model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
{ model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
{ model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
placeholder: 'Welcome to CKEditor 5!',
fontFamily: {
options: [
'Arial, Helvetica, sans-serif',
'Courier New, Courier, monospace',
'Georgia, serif',
'Lucida Sans Unicode, Lucida Grande, sans-serif',
'Tahoma, Geneva, sans-serif',
'Times New Roman, Times, serif',
'Trebuchet MS, Helvetica, sans-serif',
'Verdana, Geneva, sans-serif'
supportAllValues: true
fontSize: {
options: [ 10, 12, 14, 'default', 18, 20, 22 ],
supportAllValues: true
// Be careful with the setting below. It instructs CKEditor to accept ALL HTML markup.
htmlSupport: {
allow: [
name: /.*/,
attributes: true,
classes: true,
styles: true
codeBlock: {
languages: [
{ language: 'php', label: 'Php' },
{ language: 'js', label: 'Js' }
// Be careful with enabling previews
htmlEmbed: {
showPreviews: true
link: {
decorators: {
addTargetToExternalLinks: true,
defaultProtocol: 'https://',
toggleDownloadable: {
mode: 'manual',
label: 'Downloadable',
attributes: {
download: 'file'
mention: {
feeds: [
marker: '@',
feed: [
'@apple', '@bears', '@brownie', '@cake', '@cake', '@candy', '@canes', '@chocolate', '@cookie', '@cotton', '@cream',
'@cupcake', '@danish', '@donut', '@dragée', '@fruitcake', '@gingerbread', '@gummi', '@ice', '@jelly-o',
'@liquorice', '@macaroon', '@marzipan', '@oat', '@pie', '@plum', '@pudding', '@sesame', '@snaps', '@soufflé',
'@sugar', '@sweet', '@topping', '@wafer'
minimumCharacters: 1
simpleUpload: {
// The URL that the images are uploaded to.
uploadUrl: '{{ route('moonshine.attachments') }}',
// The "super-build" contains more premium features that require additional configuration, disable them below.
// Do not turn them on unless you read the documentation and know how to configure them and setup the editor.
removePlugins: [
// These two are commercial, but you can try them out without registering to a trial.
// 'ExportPdf',
// 'ExportWord',
// This sample uses the Base64UploadAdapter to handle image uploads as it requires no configuration.
// Storing images as Base64 is usually a very bad idea.
// Replace it on production website with other solutions:
// 'Base64UploadAdapter',
// Careful, with the Mathtype plugin CKEditor will not load when loading this sample
// from a local file system (file://) - load this site via HTTP server if you enable MathType
.ck-editor__editable {
max-height: 400px;
background-color: white!important;
color: black!important;

That's it!

# Custom Action

MoonShine comes with several built-in actions such as Export и Import but you can also create your own custom actions.

To do this, you need to create a class that extends the MoonShine action class and define the handle method.

namespace App\MoonShine\Actions;
use MoonShine\Actions\Action;
class CustomAction extends Action
public function handle(): mixed
// Code with the handler logic

This is enough to display our custom action on the resource page. However, let's take a look at what else we can define in our action class.

class CustomAction extends Action
protected static string $view = 'view.custom'; // Custom blade mapping
protected bool $withQuery = true; // Whether to pass the entire current getQuery to the action's URL.
protected bool $inDropdown = false; // Display the button outside the dropdown menu.
protected ?string $icon = 'heroicons.outline.table-cells'; // Icon for the button.

Next, register the action in the actions method of the resource where you want to display it.

public function actions(): array
return [
CustomAction::make('Custom Action'),

That's it!