Passing optional variable values into an include template Craft noob here. I have an include file which I'm calling a "component" so I can reuse it across the site.

The component is set up like this.

&lt;h2&gt;{{ heading }}&lt;/h2&gt;
{{ bodyContent }}
{% if ctaUrl is defined %}
  &lt;a class="primary link" href="{{ ctaUrl }}"&gt;Click&lt;/a&gt;
{% endif %}


Then on the template, I include it and pass some variables. In this case, the variables are from a Matrix field.

{% set matrixBlocks = entry.fullContentMatrix.all() %}

{% for block in matrixBlocks %}
    {% if block.type == 'imageRight' %}

        {% include '_components/imageRight' with {
            heading: block.heading,
            bodyContent: block.bodyContent,
            ctaUrl: block.ctaUrl
        } %}

...etc


This all works great. Until I have fields that are optional and have no value, in this case, the ctaUrl. 

What is the best way to write this so that variables are only passed to the include if they are defined?
So in this case with ctaUrl, the issue is that you are checking if ctaUrl is defined. 

As long as a field exists, is defined will return true as in reality, the variable block.ctaUrl exists, it's just empty. To check for content in your case, you should test for NULL. So if ctaUrl is not NULL show your link.

In terms of organisation of your code, it's up to you. Like Roi said, you don't need the with. Your matrixBlocks will be available within the component. 

Whether you set stuff up in the main template is up to you. Personally, I would do my logic in the component rather than the main templates. 

You will need to be consistant with your naming but the component is where you need this logic so if it's is in there, you don't need to dig around to understand what is going on. If you remove a component, the logic goes with it as suppose to have a bloated main template and also, you don't do a lot of stuff before you need to. No need in checking if you have a ctaUrl if your _components/imageRight isn't requested... 

One reason for doing the logic in the main template would be that you would have less duplication (maybe) but from experience, I'm not sure it's worth it. So In your case:

{% set matrixBlocks = entry.fullContentMatrix.all() %}

{% for block in matrixBlocks %}
    {% if block.type == 'imageRight' %}
        {% include '_components/imageRight' %}
    {% elseif block.type == 'imageLeft' %}
        {% include '_components/imageLeft' %}
    {% elseif block.type == 'someOtherBlock' %}
        ....
    {% endif %}
{% endfor %}


Then do your work in your components:

&lt;h2&gt;{{ block.heading }}&lt;/h2&gt;
{{ block.bodyContent }}
{% if ctaUrl is not NULL %}
  &lt;a class="primary link" href="{{ block.ctaUrl }}"&gt;Click&lt;/a&gt;
{% endif %}


Doing this, you will have a lean and clear main template and the component level logic will live in the component where it belongs. My opinion only though... Plenty of ways to do this :)

So to your question:


  What is the best way to write this so that variables are only passed to the include if they are defined?


In your case, don't worry about that, check if they are defined when you need to use them, at component level.